# 你看過 Elixir 嗎？如果沒有，現在讓你看看！

> Elixir 是一個令人愉快的函數式程式語言，具有許多有趣的功能。它支援管道操作符，允許清晰的函數串聯，並具有強大的元編程功能，使您能夠自定義語法。Elixir 的簡潔語法和 Ruby 的相似性對 Ruby 開發者來說尤其友好。此外，Elixir 具有 Phoenix 框架，可用於 Web 開發。

Published: 2017-10-23
URL: https://kaochenlong.com/have-you-met-elixir

---

你看過 Elixir 嗎？如果沒有，現在讓你看看！

Elixir 這個名字，對女生朋友來說也許比較熟悉一些，因為它是一款知名化妝品廠商所推出的某一種[保養品的品牌](http://www.shiseido.com.tw/brand.aspx?b=2#/new)，但我並沒有接廠商的業配而且自己也沒在用保養品，所以今天也不是要介紹這款保養品。

我第一次認識 Elixir 這個單字，其實是因為當年玩 Final Fantasy 這款遊戲時候，遊戲裡有一款叫做「エリクサー」的道具，使用後會恢復全部的 HP 跟 MP，而這個「エリクサー」其實就是從 Elixir 這個英文字轉變來的日文外來語。

這篇文章要介紹的，是一款名為 Elixir 的電腦程式語言。本文的目的並不是要告訴你 Elixir 有多好多棒、效能有多好多棒（雖然是事實），而僅介紹一些我認為這個程式語言讓我覺得寫起來很開心的地方，特別是對一個喜歡 Ruby 的人來說。

首先，學習一門新的程式語言，先不說別的，外表看得順眼是很重要的。Elixir 這款程式語言的 Logo 是一顆紫色的水滴：

![Elixir Logo](/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWnc9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--9780365cb24cdc59cbfeb637447ce43b0db6b8a2/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--578d6799c87a604ca574298502ba874c9075e929/elixir-logo.png)

好吧，我個人覺得 Logo 還不錯看（我的看法啦）。

Elixir 是一款函數型的程式語言，跟其它物件導向的程式語言比起來，Elixir 沒有類別、沒有物件、沒有所謂的實體方法、類別方法，只有一般平鋪直敘的函數（Function）。

什麼是函數？我們國、高中的數學都應該看過這樣的東西：

```
f(x) = 3x + 2
```

這個一元一次方程式，就是一個函數。函數通常會有輸入值也有輸出值，以上面這個為例，它的「輸入值」是 `x`，「輸出值」是 `3x + 2`。

在 Elixir，函數是一等公民，甚至可以把函數傳給另一個函數當做一般的參數。不過少了我們平常習慣的物件導向的觀念，一開始真的有點不習慣，會不知道該把程式寫在哪邊比較好。

Elixir 這個程式語言可以介紹的內容很多，應該不是我這一篇文章就可以聊完的，更多關於這個程式語言的細節，請各位移駕至 [Elixir 的官網](https://elixir-lang.org/)，在這邊就一些我個人覺得有趣、好玩的語法跟大家分享。

## 好玩的語法

### Pipe Operator

也許你曾看過這樣的程式碼：

```
func1(func2(func3(super_function(best_function(&quot;I love you&quot;)))))
```

先不管這個 `func1`、`func2` 或是 `super_function` 是在幹嘛，像這樣洋蔥式或大腸包小腸式一層包一層的寫法，雖然仔細看還是能知道意思，但可讀性上就稍微沒那麼好。如果改成多行，可以變這樣：

```
tmp1 = best_function(&quot;I love you&quot;)
tmp2 = super_function(tmp1)
tmp3 = func3(tmp2)
tmp4 = func2(tmp3)
tmp5 = func1(tmp4)
```

老實說這樣寫也沒好多少，特別如果又像我這樣故意用一堆之後用不上的 temp 變數。

Elixir 可以直接這樣寫：

```elixir
&quot;I love you&quot;
|&gt; best_function
|&gt; super_function
|&gt; func3
|&gt; func2
|&gt; func1
```

Elixir 的 `|&gt;` 這個 Operator，意思就是「把前一個 function 執行的結果，當做第一個參數傳入下一個 function」，所以下面這段 Elixir 範例：

```elixir
&quot;ruby is awesome!&quot;
|&gt; String.split
|&gt; Enum.map(fn(x) -&gt; String.capitalize(x) end)
|&gt; Enum.join(&quot; &quot;)

# 得到 &quot;Ruby Is Awesome!&quot;
```

它做了「把字串接開」→「把每個元素的首字改大寫」→「把各個元素組回字串」。其中中間 `Enum.map/2` 那段可以再改成這樣：

```elixir
&quot;ruby is awesome!&quot;
|&gt; String.split
|&gt; Enum.map(&amp;(String.capitalize/1))
|&gt; Enum.join(&quot; &quot;)
```

像這樣可以把前一個 function 執行的結果，一路往下一個 function 接下去，可以讓每個 function 的「任務」分得很清楚，隨時可以再增加或刪除，個人覺得這樣超有趣的！

### Metaprogramming

習慣 Ruby 之後，程式語言有沒有直接支援 Metaprogramming 的功能對我來說就會變得很重要。Elixir 直接就有支援 Metaprogramming：

其實我不太確定 Metaprogramming 該怎麼翻成中文比較適當，不過請大家先看看這張截圖：

![Elixir](/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWnM9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--1c979edc803ae213ba51775069835dcc6570252c/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--578d6799c87a604ca574298502ba874c9075e929/elixir_made_by_elixir.png)

從帳面上的數字看起來，放在 GitHub 的 Elixir 程式語言專案，有 89.7% 的內容是用 Elixir 這個程式語言寫的。咦？有覺得哪裡怪怪的嗎？也就是說這個程式語言，有將近九成的組成都是 Elixir 這個程式語言本身。用自己寫自己，我覺得這超酷的！

在 Elixir 要使用 Metaprogramming 的技巧，主要都是使用 `macro` 搭配 `quote` 跟 `unquote` 來撰寫：

再從 Metaprogramming Elixir 書上借個例子：

```elixir
defmodule Math do
  defmacro say({:+, _, [lhs, rhs]}) do
    quote do
      IO.puts &quot;#{unquote(lhs)} 加 #{unquote(rhs)} 等於 #{unquote(lhs + rhs)}&quot;
    end
  end

  defmacro say({:*, _, [lhs, rhs]}) do
    quote do
      IO.puts &quot;#{unquote(lhs)} 乘以 #{unquote(rhs)} 等於 #{unquote(lhs * rhs)}&quot;
    end
  end
end
```

先不管其實這程式看起來怎麼樣（好啦，其實有點不太容易懂），但用起來的樣子像這樣：

```
$ iex math.exs
Erlang/OTP 20 [erts-9.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)&gt; require Math
Math
iex(2)&gt; Math.say 1 + 2
1 加 2 等於 3
:ok
iex(3)&gt; Math.say 2 * 3
2 乘以 3 等於 6
:ok
```

發生什麼事了？ 我先把 `1 + 2` 透過 `quote` 拆解成這樣：

```
iex(1)&gt; quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
```

這等於是直接在操作 AST（Abstract Syntax Tree）了，這超酷的。可以做到這種層級的操作，我想大概什麼功能都能寫得出來了。

其實在 Macro 在 Elixir 的應用非常廣，甚至後來有次無聊去翻 Elixir 的原始碼才發現，原來在 Elixir 裡，連 `if...else` 跟 `unless` 之類看起來像關鍵字的語法，就是用 Macro 的方式寫出來的：

```elixir
# 檔案位置：lib/elixir/lib/kernel.ex
defmacro if(condition, clauses) do
  build_if(condition, clauses)
end

defp build_if(condition, do: do_clause) do
  build_if(condition, do: do_clause, else: nil)
end

defp build_if(condition, do: do_clause, else: else_clause) do
  optimize_boolean(
    quote do
      case unquote(condition) do
        x when x in [false, nil] -&gt; unquote(else_clause)
        _ -&gt; unquote(do_clause)
      end
    end
  )
end

defp build_if(_condition, _arguments) do
  raise ArgumentError,
        &quot;invalid or duplicate keys for if, only \&quot;do\&quot; and an optional \&quot;else\&quot; are permitted&quot;
end
```

如果你仔細看上面這段原始碼，就會發現在 Elixir 的 `if ... else` 其實就是 `case ... when` 而已，這真的太有趣了 :)

對 Macro 有興趣的話，可以再參考官網上的[文件](https://elixir-lang.org/getting-started/meta/macros.html)。

### 其它我覺得有趣的...

可以省略小括號

這對別的程式語言的人來說可能沒什麼感覺，但對寫習慣 Ruby 的人來卻是一大福音。

有 REPL 可以用

REPL 是「Read-Eval-Print Loop」幾個字的縮寫，許多程式語言都有提供這樣的工具，以 Ruby 來說是 `irb`，而在 Elixir 則是 `iex`。在終端機環境下輸入 `iex` 指令後會進入互動式的環境，可以在這裡試驗或練習程式語法：

&lt;img src=&quot;/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWm89IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--44aa919366e0f0db37ca94418e44ee0eefce965b/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--578d6799c87a604ca574298502ba874c9075e929/iex.png&quot; alt=&quot;iex&quot; width=&quot;75%&quot;&gt;

跟 Ruby 的語法有像

這對已經習慣 Ruby 語法的人來說算是福音，但別誤會，即使從 Ruby 轉換到 Elixir 也不會是完全無痛，畢竟 Elixir 是一款函數式的程式語言，沒有物件導向的那些類別、物件的東西，這點需要花一點時間習慣。

## 小結

### 學 Elixir 可以幹嘛

學這個可以幹嘛喔？想幹嘛就幹嘛，學習本來就不需要為公司、不需要為長官、同事、不需要為別人，只為你自己，你自己覺得有趣最重要！

話是這樣說沒錯，但實際一點，你爸媽可能還是會想問你學這東西要幹嘛？學這個找得到工作嗎？

如果想要開發網站應用程式，有 [Phoenix](http://phoenixframework.org/) 這款 Web 開發框架可以用，Phoenix 較早版本跟 Ruby on Rails 有 87% 像，對現有的 Rails Developer 來說還算是滿友善的（但改版後長得有點不太一樣了）；另外想玩硬體，也有 [Nerves](http://nerves-project.org/) 可以研究一下。

### 聚會

目前 Elixir 在全世界的人口數還不算多，就我觀察，在台灣應該就只有一小群人在用。不過每個月都有[聚會](https://www.meetup.com/Taipei-Elixir-Erlang-Meetup-Group/)，目前也有自己專屬的 [Facebook 粉絲專頁](https://www.facebook.com/groups/elixir.tw/)。

&lt;img src=&quot;/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWms9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--06ed9961ee560a03a1da391af9b0934f655ec25b/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJYW5CbkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--c7384179b79b360ce488e92f67a9c0795f833455/elixir_ad.jpg&quot; alt=&quot;Elixir AD&quot; width=&quot;75%&quot;&gt;

圖片來源：[Shiseido 官網](http://www.shiseido.com.tw/)

借用該知名保養品品牌的宣傳台詞「遇見『水玉光』，遇見更好的自己」，小改一下變成：

&gt; 遇見 Elixir，遇見更好的自己！

### 研討會

雖然在台灣的使用人口還不算多，但我們明年在辦 RubyConf Taiwan 的時候，會把 Elixir 也正式的納入議程（就說 Ruby 跟 Elixir 是好朋友了）

反正在國外的 RubyConf 也越來越多的 Elixir 議程，即然這樣，我們就光明正大把辦在一起吧！

&lt;img src=&quot;/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWmc9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--b9c3596a4189a9f501443385e764f89ae2df5748/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJXa0NBQVE9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--578d6799c87a604ca574298502ba874c9075e929/ruby-elixir-conf.png&quot; alt=&quot;RubyElixirConf Taiwan&quot; width=&quot;75%&quot;&gt;

除了 Ruby 程式語言的老爸[松本行弘](https://twitter.com/yukihiro_matz)之外，也邀請到了 Elixir 的老爸 [José Valim](https://twitter.com/josevalim) 來擔任主題演講。咦？說到底這篇竟然是 [Ruby X Elixir Conf](https://2018.rubyconf.tw/) 的業配文 :)

