你看過 Elixir 嗎?如果沒有,現在讓你看看!

你看過 Elixir 嗎?如果沒有,現在讓你看看!
credit: Patrick Wallace

你看過 Elixir 嗎?如果沒有,現在讓你看看!

Elixir 這個名字,對女生朋友來說也許比較熟悉一些,因為它是一款知名化妝品廠商所推出的某一種保養品的品牌,但我並沒有接廠商的業配而且自己也沒在用保養品,所以今天也不是要介紹這款保養品。

我第一次認識 Elixir 這個單字,其實是因為當年玩 Final Fantasy 這款遊戲時候,遊戲裡有一款叫做「エリクサー」的道具,使用後會恢復全部的 HP 跟 MP,而這個「エリクサー」其實就是從 Elixir 這個英文字轉變來的日文外來語。

這篇文章要介紹的,是一款名為 Elixir 的電腦程式語言。本文的目的並不是要告訴你 Elixir 有多好多棒、效能有多好多棒(雖然是事實),而僅介紹一些我認為這個程式語言讓我覺得寫起來很開心的地方,特別是對一個喜歡 Ruby 的人來說。

首先,學習一門新的程式語言,先不說別的,外表看得順眼是很重要的。Elixir 這款程式語言的 Logo 是一顆紫色的水滴:

Elixir Logo

好吧,我個人覺得 Logo 還不錯看(我的看法啦)。

Elixir 是一款函數型的程式語言,跟其它物件導向的程式語言比起來,Elixir 沒有類別、沒有物件、沒有所謂的實體方法、類別方法,只有一般平鋪直敘的函數(Function)。

什麼是函數?我們國、高中的數學都應該看過這樣的東西:

f(x) = 3x + 2

這個一元一次方程式,就是一個函數。函數通常會有輸入值也有輸出值,以上面這個為例,它的「輸入值」是 x,「輸出值」是 3x + 2

在 Elixir,函數是一等公民,甚至可以把函數傳給另一個函數當做一般的參數。不過少了我們平常習慣的物件導向的觀念,一開始真的有點不習慣,會不知道該把程式寫在哪邊比較好。

Elixir 這個程式語言可以介紹的內容很多,應該不是我這一篇文章就可以聊完的,更多關於這個程式語言的細節,請各位移駕至 Elixir 的官網,在這邊就一些我個人覺得有趣、好玩的語法跟大家分享。

好玩的語法

Pipe Operator

也許你曾看過這樣的程式碼:

func1(func2(func3(super_function(best_function("I love you")))))

先不管這個 func1func2 或是 super_function 是在幹嘛,像這樣洋蔥式或大腸包小腸式一層包一層的寫法,雖然仔細看還是能知道意思,但可讀性上就稍微沒那麼好。如果改成多行,可以變這樣:

tmp1 = best_function("I love you")
tmp2 = super_function(tmp1)
tmp3 = func3(tmp2)
tmp4 = func2(tmp3)
tmp5 = func1(tmp4)

老實說這樣寫也沒好多少,特別如果又像我這樣故意用一堆之後用不上的 temp 變數。

Elixir 可以直接這樣寫:

"I love you"
|> best_function
|> super_function
|> func3
|> func2
|> func1

Elixir 的 |> 這個 Operator,意思就是「把前一個 function 執行的結果,當做第一個參數傳入下一個 function」,所以下面這段 Elixir 範例:

"ruby is awesome!"
|> String.split
|> Enum.map(fn(x) -> String.capitalize(x) end)
|> Enum.join(" ")

# 得到 "Ruby Is Awesome!"

它做了「把字串接開」→「把每個元素的首字改大寫」→「把各個元素組回字串」。其中中間 Enum.map/2 那段可以再改成這樣:

"ruby is awesome!"
|> String.split
|> Enum.map(&(String.capitalize/1))
|> Enum.join(" ")

像這樣可以把前一個 function 執行的結果,一路往下一個 function 接下去,可以讓每個 function 的「任務」分得很清楚,隨時可以再增加或刪除,個人覺得這樣超有趣的!

Metaprogramming

習慣 Ruby 之後,程式語言有沒有直接支援 Metaprogramming 的功能對我來說就會變得很重要。Elixir 直接就有支援 Metaprogramming:

其實我不太確定 Metaprogramming 該怎麼翻成中文比較適當,不過請大家先看看這張截圖:

Elixir

從帳面上的數字看起來,放在 GitHub 的 Elixir 程式語言專案,有 89.7% 的內容是用 Elixir 這個程式語言寫的。咦?有覺得哪裡怪怪的嗎?也就是說這個程式語言,有將近九成的組成都是 Elixir 這個程式語言本身。用自己寫自己,我覺得這超酷的!

在 Elixir 要使用 Metaprogramming 的技巧,主要都是使用 macro 搭配 quoteunquote 來撰寫:

再從 Metaprogramming Elixir 書上借個例子:

defmodule Math do
  defmacro say({:+, _, [lhs, rhs]}) do
    quote do
      IO.puts "#{unquote(lhs)} 加 #{unquote(rhs)} 等於 #{unquote(lhs + rhs)}"
    end
  end

  defmacro say({:*, _, [lhs, rhs]}) do
    quote do
      IO.puts "#{unquote(lhs)} 乘以 #{unquote(rhs)} 等於 #{unquote(lhs * rhs)}"
    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)> require Math
Math
iex(2)> Math.say 1 + 2
1 加 2 等於 3
:ok
iex(3)> Math.say 2 * 3
2 乘以 3 等於 6
:ok

發生什麼事了? 我先把 1 + 2 透過 quote 拆解成這樣:

iex(1)> quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}

這等於是直接在操作 AST(Abstract Syntax Tree)了,這超酷的。可以做到這種層級的操作,我想大概什麼功能都能寫得出來了。

其實在 Macro 在 Elixir 的應用非常廣,甚至後來有次無聊去翻 Elixir 的原始碼才發現,原來在 Elixir 裡,連 if...elseunless 之類看起來像關鍵字的語法,就是用 Macro 的方式寫出來的:

# 檔案位置: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] -> unquote(else_clause)
        _ -> unquote(do_clause)
      end
    end
  )
end

defp build_if(_condition, _arguments) do
  raise ArgumentError,
        "invalid or duplicate keys for if, only \"do\" and an optional \"else\" are permitted"
end

如果你仔細看上面這段原始碼,就會發現在 Elixir 的 if ... else 其實就是 case ... when 而已,這真的太有趣了 :)

對 Macro 有興趣的話,可以再參考官網上的文件

其它我覺得有趣的...

可以省略小括號

這對別的程式語言的人來說可能沒什麼感覺,但對寫習慣 Ruby 的人來卻是一大福音。

有 REPL 可以用

REPL 是「Read-Eval-Print Loop」幾個字的縮寫,許多程式語言都有提供這樣的工具,以 Ruby 來說是 irb,而在 Elixir 則是 iex。在終端機環境下輸入 iex 指令後會進入互動式的環境,可以在這裡試驗或練習程式語法:

iex

跟 Ruby 的語法有像

這對已經習慣 Ruby 語法的人來說算是福音,但別誤會,即使從 Ruby 轉換到 Elixir 也不會是完全無痛,畢竟 Elixir 是一款函數式的程式語言,沒有物件導向的那些類別、物件的東西,這點需要花一點時間習慣。

小結

學 Elixir 可以幹嘛

學這個可以幹嘛喔?想幹嘛就幹嘛,學習本來就不需要為公司、不需要為長官、同事、不需要為別人,只為你自己,你自己覺得有趣最重要!

話是這樣說沒錯,但實際一點,你爸媽可能還是會想問你學這東西要幹嘛?學這個找得到工作嗎?

如果想要開發網站應用程式,有 Phoenix 這款 Web 開發框架可以用,Phoenix 較早版本跟 Ruby on Rails 有 87% 像,對現有的 Rails Developer 來說還算是滿友善的(但改版後長得有點不太一樣了);另外想玩硬體,也有 Nerves 可以研究一下。

聚會

目前 Elixir 在全世界的人口數還不算多,就我觀察,在台灣應該就只有一小群人在用。不過每個月都有聚會,目前也有自己專屬的 Facebook 粉絲專頁

Elixir AD

圖片來源:Shiseido 官網

借用該知名保養品品牌的宣傳台詞「遇見『水玉光』,遇見更好的自己」,小改一下變成:

遇見 Elixir,遇見更好的自己!

研討會

雖然在台灣的使用人口還不算多,但我們明年在辦 RubyConf Taiwan 的時候,會把 Elixir 也正式的納入議程(就說 Ruby 跟 Elixir 是好朋友了)

反正在國外的 RubyConf 也越來越多的 Elixir 議程,即然這樣,我們就光明正大把辦在一起吧!

RubyElixirConf Taiwan

除了 Ruby 程式語言的老爸松本行弘之外,也邀請到了 Elixir 的老爸 José Valim 來擔任主題演講。

咦?說到底這篇竟然是 Ruby X Elixir Conf 的業配文 :)