在京東上搜Elixir向楼,會(huì)發(fā)現(xiàn)Elixir是一種琴弦的品牌查吊,然而我今天想講的Elixir是一種編程語(yǔ)言。它的Logo如下
早就有學(xué)習(xí)這門語(yǔ)言的打算湖蜕,只是事情太多逻卖,忙著忙著就忘了。公司最近的技術(shù)分享中重荠,明哥分享了一個(gè)關(guān)于Phoenix(基于Elixir的Web開(kāi)發(fā)框架)跟Rails的比較的話題箭阶,重新燃起了我對(duì)這門語(yǔ)言的好奇心。接下來(lái)讓我們一起簡(jiǎn)單的來(lái)看看Elixir這門語(yǔ)言戈鲁。
1. Elixir是什么
我們先來(lái)看看官方的解釋
Elixir is a dynamic, functional language designed for building scalable and maintainable applications.
Elixir 是一門動(dòng)態(tài)的函數(shù)式編程語(yǔ)言,主要是用來(lái)構(gòu)建可擴(kuò)展嘹叫,可維護(hù)的應(yīng)用程序婆殿。好吧,把函數(shù)式去掉的話Ruby也可以做到罩扇。Ruby是一門動(dòng)態(tài)的命令式編程語(yǔ)言婆芦,可以用來(lái)構(gòu)建可擴(kuò)展及可維護(hù)的應(yīng)用程序怕磨。不過(guò)Ruby設(shè)計(jì)的初衷并不是為了解決什么問(wèn)題,它的初衷是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運(yùn)行在Erlang虛擬機(jī)上消约,以低負(fù)載肠鲫,分布式和容錯(cuò)系統(tǒng)而聞名。已經(jīng)成功應(yīng)用在Web開(kāi)發(fā)領(lǐng)域或粮,以及嵌入式軟件領(lǐng)域导饲。這也算是編程語(yǔ)言的另一個(gè)發(fā)展方向吧,除了直接用系統(tǒng)級(jí)編程語(yǔ)言(如C氯材,C++渣锦,Go)來(lái)開(kāi)發(fā)編程語(yǔ)言,我們還可以在比較成熟的虛擬系統(tǒng)上構(gòu)造我們想要的編程語(yǔ)言氢哮。比如JavaVM之上有人發(fā)明了Clojure袋毙,Scale這些函數(shù)式編程語(yǔ)言(身邊的人好像比較喜歡Clojure),而有位Ruby英雄在ErlangVM上實(shí)現(xiàn)了Elixir冗尤。
2. 安裝Elixir
安裝教程沒(méi)有能比官方文檔更加詳細(xì)的了听盖,我這里就不重復(fù)說(shuō)了,我就說(shuō)說(shuō)我在Mac上安裝的時(shí)候遇到的比較尷尬的問(wèn)題吧裂七。
Elixir是運(yùn)行在Erlang環(huán)境下的編程語(yǔ)言媳溺,當(dāng)我們用HomeBrew安裝Elixir的時(shí)候,它也會(huì)順勢(shì)幫你安裝Erlang碍讯。
想想很久之前我們用HomeBrew安裝了Elixir悬蔽,以及配套的Erlang環(huán)境。然后我們卸載了Elixir捉兴,卻留下了Erlang蝎困。幾個(gè)月后我們重新安裝Elixir這個(gè)時(shí)候,也配套安裝了最新版本的Erlang倍啥,于是事故發(fā)生了禾乘。
> 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>
我們的系統(tǒng)同時(shí)保留著兩個(gè)版本的Erlang,以及新版本的Elixir虽缕。而系統(tǒng)還默認(rèn)引用這舊版本的Erlang始藕,這表明了我們會(huì)把新版本的Elixir運(yùn)行在舊版本的Erlang上面,你可能就會(huì)得到如下錯(cuò)誤氮趋。
> 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: []
這是個(gè)錯(cuò)誤示范伍派,可能你也會(huì)遇到類似的問(wèn)題,報(bào)錯(cuò)信息不會(huì)完全一樣剩胁,不過(guò)應(yīng)該也差不多诉植。究其原因就如上面所說(shuō)Eixir版本跟Erlang版本不兼容,當(dāng)我們想在老版本的Erlang上跑新版本的Elixir就出問(wèn)題了昵观。
解決方法有兩個(gè)
1) 切換Erlang的默認(rèn)版本晾腔,換成我們期待的最新版本的Erlang
我們通過(guò)HomeBrew的命令
brew switch erlang 20.1.3
只需要簡(jiǎn)單地把默認(rèn)Erlang運(yùn)行環(huán)境指定到我們需要的最新版本的舌稀,然后重新運(yùn)行Elixir就能夠進(jìn)入我們期待的REPL環(huán)境了。
> 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壁查,重新安裝
這種方法簡(jiǎn)單粗暴,如果我們不需要多版本的Erlang的話我建議可以把他們都移除掉剔应,畢竟一個(gè)Erlang包會(huì)占用掉系統(tǒng)200多MB的磁盤空間睡腿。
通過(guò)brew
命令的uninstall
指令,加上--force
標(biāo)識(shí)就能夠移除所有版本的Erlang
brew uninstall --force erlang
如果單獨(dú)移除Erlang而不移除Elixir的話就會(huì)有以下的異常信息
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
前面也說(shuō)了Elixir依賴于Erlang领斥,當(dāng)我們只想單獨(dú)卸載被依賴的Erlang的時(shí)候就會(huì)有這個(gè)警告嫉到,我不建議用它的命令保留Elixir而單獨(dú)移除Erlang,畢竟該清理的還是要清理干凈月洛,重新安裝也耗費(fèi)不了多少時(shí)間何恶。我們只需要依次運(yùn)行以下命令
brew uninstall elixir
brew uninstall --force erlang
brew install elixir
把Elixir以及所有版本的Erlang刪除之后,再重新安裝Elixir嚼黔,我們就可以體驗(yàn)最新版本的Elixir细层,并開(kāi)始美妙的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程序
又到了大家喜聞樂(lè)見(jiàn)的活動(dòng)了唬涧,第一條程序總是會(huì)讓人熱血沸騰疫赎。不過(guò)咱門今天就別上HelloWorld了吧,來(lái)簡(jiǎn)單寫一個(gè)腳本碎节。
(1) 編譯模式
寫習(xí)慣了解釋性語(yǔ)言捧搞,已經(jīng)很久沒(méi)試過(guò)編譯代碼了。想想以前寫C的時(shí)候運(yùn)行之前都是需要先編譯狮荔,然后執(zhí)行生成的二進(jìn)制文件√テ玻現(xiàn)在我們來(lái)看看Elixir是如何操作的。
Elixir有個(gè)約定殖氏,需要編譯的文件以.ex
后綴名結(jié)尾晚树,而直接運(yùn)行的腳本則.exs
后綴名結(jié)尾,我們也依照這個(gè)約定吧(即便我們都知道在Unix操作系統(tǒng)里面后綴名是什么根本就無(wú)所謂)雅采。這里我們創(chuàng)建一個(gè)文件math.ex
# math.ex
defmodule Math do
def sum(a, b) do
a + b
end
end
這種編碼風(fēng)格跟Ruby很像爵憎,實(shí)際上我們定義了一個(gè)包含sum方法的模塊Math,然后運(yùn)行編譯命令
> elixirc math.ex
編譯成功后會(huì)生成名為Elixir.Math.beam
的文件
> ls
Elixir.Math.beam math.ex
這個(gè)就是編譯后的字節(jié)碼文件婚瓜,熟悉Java的人應(yīng)該知道Java里面也有類似的存在宝鼓。然后我們?cè)诋?dāng)前目錄下面運(yùn)行iex
進(jìn)行REPL環(huán)境,就能直接使用這個(gè)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
接下來(lái)再簡(jiǎn)單看看腳本模式闰渔。
(2) 腳本模式
每次都要編譯那多麻煩啊席函,很好浸策,這也是我不喜歡部分編譯型語(yǔ)言的原因之一(只是部分)---它們甚至連REPL環(huán)境都沒(méi)有灿椅。Elixir考慮到了這一點(diǎn),按照習(xí)俗我們?cè)谛履夸浵聞?chuàng)建一個(gè)math.exs
的腳本文件
# math.exs
defmodule Math do
def sum(a, b) do
a + b
end
end
PS: 內(nèi)容跟編譯模式中的文件一樣抚太,只是后綴名不同督弓。
> ls
math.exs
然后我們可以直接用相關(guān)的命令運(yùn)行對(duì)應(yīng)腳本
> elixir math.exs
由于我們math.exs
里面只是定義了一個(gè)簡(jiǎn)單的模塊和方法营曼,并沒(méi)有更多的邏輯,所以這次執(zhí)行也看不出什么效果愚隧。不過(guò)在Elixir機(jī)制中我們可以像下面這樣操作蒂阱,直接地把目標(biāo)文件加載到REPL環(huán)境中
> 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
可以看到運(yùn)行結(jié)果跟之前編譯模式一樣的只是少了手動(dòng)編譯那一步,這有利于我們對(duì)源碼文件進(jìn)行簡(jiǎn)單地測(cè)試狂塘,而不需要每次測(cè)試的時(shí)候都手動(dòng)編譯一次录煤。
官網(wǎng)的建議是平時(shí)開(kāi)發(fā)業(yè)務(wù)邏輯代碼都以
.ex
后綴名結(jié)尾,最后需要被編譯成字節(jié)碼(這可能是性能方面考慮吧)荞胡。而編寫日常單元測(cè)試或者配置信息則以.exs
后綴名來(lái)結(jié)尾妈踊。
4. 總結(jié)
對(duì)這門語(yǔ)言的學(xué)習(xí),大概只是看了4-5天的入門指南泪漂。它是否真如文檔所說(shuō)能夠用來(lái)構(gòu)建可擴(kuò)展性高的系統(tǒng)廊营,以及它是否真的有傳聞?wù)f的那樣地高性能呢?這一點(diǎn)還有待考證萝勤。
不過(guò)經(jīng)過(guò)這兩天的學(xué)習(xí)露筒,我覺(jué)得這是一門挺值得入手的語(yǔ)言,特別是對(duì)于Ruby系的朋友來(lái)說(shuō)敌卓。如果說(shuō)Haskell是有Python風(fēng)格的純函數(shù)式編程語(yǔ)言(他們大寫的None讓我印象深刻)慎式,那我覺(jué)得Elixir就是有Ruby風(fēng)格的函數(shù)式編程語(yǔ)言了。不過(guò)即便它語(yǔ)法風(fēng)格設(shè)計(jì)方面跟Ruby很像趟径,但Elixir就是Elixir瘪吏,相信你會(huì)從中感受到與Ruby不一樣的編程體驗(yàn)。