在京東上搜Elixir,會發現Elixir是一種琴弦的品牌,然而我今天想講的Elixir是一種編程語言。它的Logo如下
早就有學習這門語言的打算,只是事情太多,忙著忙著就忘了。公司最近的技術分享中,明哥分享了一個關于Phoenix(基于Elixir的Web開發框架)跟Rails的比較的話題,重新燃起了我對這門語言的好奇心。接下來讓我們一起簡單的來看看Elixir這門語言。
1. Elixir是什么
我們先來看看官方的解釋
Elixir is a dynamic, functional language designed for building scalable and maintainable applications.
Elixir 是一門動態的函數式編程語言,主要是用來構建可擴展,可維護的應用程序。好吧,把函數式去掉的話Ruby也可以做到。Ruby是一門動態的命令式編程語言,可以用來構建可擴展及可維護的應用程序。不過Ruby設計的初衷并不是為了解決什么問題,它的初衷是Happy Coding!
Elixir leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems, while also being successfully used in web development and the embedded software domain.
Elixir運行在Erlang虛擬機上,以低負載,分布式和容錯系統而聞名。已經成功應用在Web開發領域,以及嵌入式軟件領域。這也算是編程語言的另一個發展方向吧,除了直接用系統級編程語言(如C,C++,Go)來開發編程語言,我們還可以在比較成熟的虛擬系統上構造我們想要的編程語言。比如JavaVM之上有人發明了Clojure,Scale這些函數式編程語言(身邊的人好像比較喜歡Clojure),而有位Ruby英雄在ErlangVM上實現了Elixir。
2. 安裝Elixir
安裝教程沒有能比官方文檔更加詳細的了,我這里就不重復說了,我就說說我在Mac上安裝的時候遇到的比較尷尬的問題吧。
Elixir是運行在Erlang環境下的編程語言,當我們用HomeBrew安裝Elixir的時候,它也會順勢幫你安裝Erlang。
想想很久之前我們用HomeBrew安裝了Elixir,以及配套的Erlang環境。然后我們卸載了Elixir,卻留下了Erlang。幾個月后我們重新安裝Elixir這個時候,也配套安裝了最新版本的Erlang,于是事故發生了。
> erl
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Eshell V8.3 (abort with ^G)
1>
我們的系統同時保留著兩個版本的Erlang,以及新版本的Elixir。而系統還默認引用這舊版本的Erlang,這表明了我們會把新版本的Elixir運行在舊版本的Erlang上面,你可能就會得到如下錯誤。
> iex
2017-11-02 21:54:29 Loading of ~ts failed: ~p
"/usr/local/Cellar/elixir/1.5.2/bin/../lib/iex/ebin/Elixir.IEx.CLI.beam"
badfile
2017-11-02 21:54:29 ~s~n
"beam/beam_load.c(1287): Error loading module 'Elixir.IEx.CLI':\n mandatory chunk of type 'Atom' not found\n\n"
2017-11-02 21:54:29 crash_report
initial_call: {supervisor_bridge,user_sup,['Argument__1']}
pid: <0.47.0>
registered_name: []
error_info: {exit,{undef,[{'Elixir.IEx.CLI',start,[],[]},{user_sup,start_user,3,[{file,"user_sup.erl"},{line,100}]},{user_sup,init,1,[{file,"user_sup.erl"},{line,49}]},{supervisor_bridge,init,1,[{file,"supervisor_bridge.erl"},{line,80}]},{gen_server,init_it,6,[{file,"gen_server.erl"},{line,328}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]},[{gen_server,init_it,6,[{file,"gen_server.erl"},{line,352}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
ancestors: [kernel_sup,<0.34.0>]
messages: []
links: [<0.35.0>]
dictionary: []
trap_exit: true
status: running
heap_size: 610
stack_size: 27
reductions: 145
2017-11-02 21:54:29 supervisor_report
supervisor: {local,kernel_sup}
errorContext: start_error
reason: {undef,[{'Elixir.IEx.CLI',start,[],[]},{user_sup,start_user,3,[{file,"user_sup.erl"},{line,100}]},{user_sup,init,1,[{file,"user_sup.erl"},{line,49}]},{supervisor_bridge,init,1,[{file,"supervisor_bridge.erl"},{line,80}]},{gen_server,init_it,6,[{file,"gen_server.erl"},{line,328}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
offender: [{pid,undefined},{id,user},{mfargs,{user_sup,start,[]}},{restart_type,temporary},{shutdown,2000},{child_type,supervisor}]
2017-11-02 21:54:29 crash_report
initial_call: {application_master,init,['Argument__1','Argument__2','Argument__3','Argument__4']}
pid: <0.33.0>
registered_name: []
這是個錯誤示范,可能你也會遇到類似的問題,報錯信息不會完全一樣,不過應該也差不多。究其原因就如上面所說Eixir版本跟Erlang版本不兼容,當我們想在老版本的Erlang上跑新版本的Elixir就出問題了。
解決方法有兩個
1) 切換Erlang的默認版本,換成我們期待的最新版本的Erlang
我們通過HomeBrew的命令
brew switch erlang 20.1.3
只需要簡單地把默認Erlang運行環境指定到我們需要的最新版本的,然后重新運行Elixir就能夠進入我們期待的REPL環境了。
> iex
Erlang/OTP 20 [erts-9.1.3] [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)
2) 卸載Elixir,以及所有版本的Erlang,重新安裝
這種方法簡單粗暴,如果我們不需要多版本的Erlang的話我建議可以把他們都移除掉,畢竟一個Erlang包會占用掉系統200多MB的磁盤空間。
通過brew
命令的uninstall
指令,加上--force
標識就能夠移除所有版本的Erlang
brew uninstall --force erlang
如果單獨移除Erlang而不移除Elixir的話就會有以下的異常信息
Error: Refusing to uninstall /usr/local/Cellar/erlang/20.1.3
because it is required by elixir 1.5.2, which is currently installed.
You can override this and force removal with:
brew uninstall --ignore-dependencies erlang
前面也說了Elixir依賴于Erlang,當我們只想單獨卸載被依賴的Erlang的時候就會有這個警告,我不建議用它的命令保留Elixir而單獨移除Erlang,畢竟該清理的還是要清理干凈,重新安裝也耗費不了多少時間。我們只需要依次運行以下命令
brew uninstall elixir
brew uninstall --force erlang
brew install elixir
把Elixir以及所有版本的Erlang刪除之后,再重新安裝Elixir,我們就可以體驗最新版本的Elixir,并開始美妙的Elixir之旅。
> iex
Erlang/OTP 20 [erts-9.1.3] [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)
3. 第一條Elixir程序
又到了大家喜聞樂見的活動了,第一條程序總是會讓人熱血沸騰。不過咱門今天就別上HelloWorld了吧,來簡單寫一個腳本。
(1) 編譯模式
寫習慣了解釋性語言,已經很久沒試過編譯代碼了。想想以前寫C的時候運行之前都是需要先編譯,然后執行生成的二進制文件。現在我們來看看Elixir是如何操作的。
Elixir有個約定,需要編譯的文件以.ex
后綴名結尾,而直接運行的腳本則.exs
后綴名結尾,我們也依照這個約定吧(即便我們都知道在Unix操作系統里面后綴名是什么根本就無所謂)。這里我們創建一個文件math.ex
# math.ex
defmodule Math do
def sum(a, b) do
a + b
end
end
這種編碼風格跟Ruby很像,實際上我們定義了一個包含sum方法的模塊Math,然后運行編譯命令
> elixirc math.ex
編譯成功后會生成名為Elixir.Math.beam
的文件
> ls
Elixir.Math.beam math.ex
這個就是編譯后的字節碼文件,熟悉Java的人應該知道Java里面也有類似的存在。然后我們在當前目錄下面運行iex
進行REPL環境,就能直接使用這個Math模塊了
> iex
Erlang/OTP 20 [erts-9.1.3] [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)> Math.sum(1,2)
3
接下來再簡單看看腳本模式。
(2) 腳本模式
每次都要編譯那多麻煩啊,很好,這也是我不喜歡部分編譯型語言的原因之一(只是部分)---它們甚至連REPL環境都沒有。Elixir考慮到了這一點,按照習俗我們在新目錄下創建一個math.exs
的腳本文件
# math.exs
defmodule Math do
def sum(a, b) do
a + b
end
end
PS: 內容跟編譯模式中的文件一樣,只是后綴名不同。
> ls
math.exs
然后我們可以直接用相關的命令運行對應腳本
> elixir math.exs
由于我們math.exs
里面只是定義了一個簡單的模塊和方法,并沒有更多的邏輯,所以這次執行也看不出什么效果。不過在Elixir機制中我們可以像下面這樣操作,直接地把目標文件加載到REPL環境中
> iex math.exs
Erlang/OTP 20 [erts-9.1.3] [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)> Math.sum(1,2)
3
可以看到運行結果跟之前編譯模式一樣的只是少了手動編譯那一步,這有利于我們對源碼文件進行簡單地測試,而不需要每次測試的時候都手動編譯一次。
官網的建議是平時開發業務邏輯代碼都以
.ex
后綴名結尾,最后需要被編譯成字節碼(這可能是性能方面考慮吧)。而編寫日常單元測試或者配置信息則以.exs
后綴名來結尾。
4. 總結
對這門語言的學習,大概只是看了4-5天的入門指南。它是否真如文檔所說能夠用來構建可擴展性高的系統,以及它是否真的有傳聞說的那樣地高性能呢?這一點還有待考證。
不過經過這兩天的學習,我覺得這是一門挺值得入手的語言,特別是對于Ruby系的朋友來說。如果說Haskell是有Python風格的純函數式編程語言(他們大寫的None讓我印象深刻),那我覺得Elixir就是有Ruby風格的函數式編程語言了。不過即便它語法風格設計方面跟Ruby很像,但Elixir就是Elixir,相信你會從中感受到與Ruby不一樣的編程體驗。