Elixir初體驗(yàn)

在京東上搜Elixir,會發(fā)現(xiàn)Elixir是一種琴弦的品牌,然而我今天想講的Elixir是一種編程語言。它的Logo如下

Elixir

早就有學(xué)習(xí)這門語言的打算,只是事情太多,忙著忙著就忘了。公司最近的技術(shù)分享中,明哥分享了一個(gè)關(guān)于Phoenix(基于Elixir的Web開發(fā)框架)跟Rails的比較的話題,重新燃起了我對這門語言的好奇心。接下來讓我們一起簡單的來看看Elixir這門語言。

1. Elixir是什么

我們先來看看官方的解釋

Elixir is a dynamic, functional language designed for building scalable and maintainable applications.

Elixir 是一門動態(tài)的函數(shù)式編程語言,主要是用來構(gòu)建可擴(kuò)展,可維護(hù)的應(yīng)用程序。好吧,把函數(shù)式去掉的話Ruby也可以做到。Ruby是一門動態(tài)的命令式編程語言,可以用來構(gòu)建可擴(kuò)展及可維護(hù)的應(yīng)用程序。不過Ruby設(shè)計(jì)的初衷并不是為了解決什么問題,它的初衷是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開發(fā)領(lǐng)域,以及嵌入式軟件領(lǐng)域。這也算是編程語言的另一個(gè)發(fā)展方向吧,除了直接用系統(tǒng)級編程語言(如C,C++,Go)來開發(fā)編程語言,我們還可以在比較成熟的虛擬系統(tǒng)上構(gòu)造我們想要的編程語言。比如JavaVM之上有人發(fā)明了Clojure,Scale這些函數(shù)式編程語言(身邊的人好像比較喜歡Clojure),而有位Ruby英雄在ErlangVM上實(shí)現(xiàn)了Elixir。

2. 安裝Elixir

安裝教程沒有能比官方文檔更加詳細(xì)的了,我這里就不重復(fù)說了,我就說說我在Mac上安裝的時(shí)候遇到的比較尷尬的問題吧。

Elixir是運(yùn)行在Erlang環(huán)境下的編程語言,當(dāng)我們用HomeBrew安裝Elixir的時(shí)候,它也會順勢幫你安裝Erlang。

想想很久之前我們用HomeBrew安裝了Elixir,以及配套的Erlang環(huán)境。然后我們卸載了Elixir,卻留下了Erlang。幾個(gè)月后我們重新安裝Elixir這個(gè)時(shí)候,也配套安裝了最新版本的Erlang,于是事故發(fā)生了。

兩個(gè)不同版本的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>

我們的系統(tǒng)同時(shí)保留著兩個(gè)版本的Erlang,以及新版本的Elixir。而系統(tǒng)還默認(rèn)引用這舊版本的Erlang,這表明了我們會把新版本的Elixir運(yùn)行在舊版本的Erlang上面,你可能就會得到如下錯(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ò)誤示范,可能你也會遇到類似的問題,報(bào)錯(cuò)信息不會完全一樣,不過應(yīng)該也差不多。究其原因就如上面所說Eixir版本跟Erlang版本不兼容,當(dāng)我們想在老版本的Erlang上跑新版本的Elixir就出問題了。

解決方法有兩個(gè)

1) 切換Erlang的默認(rèn)版本,換成我們期待的最新版本的Erlang

我們通過HomeBrew的命令

brew switch erlang 20.1.3

只需要簡單地把默認(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,重新安裝

這種方法簡單粗暴,如果我們不需要多版本的Erlang的話我建議可以把他們都移除掉,畢竟一個(gè)Erlang包會占用掉系統(tǒng)200多MB的磁盤空間。

通過brew命令的uninstall指令,加上--force標(biāo)識就能夠移除所有版本的Erlang

brew uninstall --force erlang

如果單獨(dú)移除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,當(dāng)我們只想單獨(dú)卸載被依賴的Erlang的時(shí)候就會有這個(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,并開始美妙的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了吧,來簡單寫一個(gè)腳本。

First

(1) 編譯模式

寫習(xí)慣了解釋性語言,已經(jīng)很久沒試過編譯代碼了。想想以前寫C的時(shí)候運(yùn)行之前都是需要先編譯,然后執(zhí)行生成的二進(jìn)制文件?,F(xiàn)在我們來看看Elixir是如何操作的。

Elixir有個(gè)約定,需要編譯的文件以.ex后綴名結(jié)尾,而直接運(yùn)行的腳本則.exs后綴名結(jié)尾,我們也依照這個(gè)約定吧(即便我們都知道在Unix操作系統(tǒng)里面后綴名是什么根本就無所謂)。這里我們創(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

編譯成功后會生成名為Elixir.Math.beam的文件

> ls
Elixir.Math.beam math.ex

這個(gè)就是編譯后的字節(jié)碼文件,熟悉Java的人應(yīng)該知道Java里面也有類似的存在。然后我們在當(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

接下來再簡單看看腳本模式。

(2) 腳本模式

每次都要編譯那多麻煩啊,很好,這也是我不喜歡部分編譯型語言的原因之一(只是部分)---它們甚至連REPL環(huán)境都沒有。Elixir考慮到了這一點(diǎn),按照習(xí)俗我們在新目錄下創(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)行對應(yīng)腳本

> elixir math.exs

由于我們math.exs里面只是定義了一個(gè)簡單的模塊和方法,并沒有更多的邏輯,所以這次執(zhí)行也看不出什么效果。不過在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é)果跟之前編譯模式一樣的只是少了手動編譯那一步,這有利于我們對源碼文件進(jìn)行簡單地測試,而不需要每次測試的時(shí)候都手動編譯一次。

官網(wǎng)的建議是平時(shí)開發(fā)業(yè)務(wù)邏輯代碼都以.ex后綴名結(jié)尾,最后需要被編譯成字節(jié)碼(這可能是性能方面考慮吧)。而編寫日常單元測試或者配置信息則以.exs后綴名來結(jié)尾。

4. 總結(jié)

對這門語言的學(xué)習(xí),大概只是看了4-5天的入門指南。它是否真如文檔所說能夠用來構(gòu)建可擴(kuò)展性高的系統(tǒng),以及它是否真的有傳聞?wù)f的那樣地高性能呢?這一點(diǎn)還有待考證。

剁手

不過經(jīng)過這兩天的學(xué)習(xí),我覺得這是一門挺值得入手的語言,特別是對于Ruby系的朋友來說。如果說Haskell是有Python風(fēng)格的純函數(shù)式編程語言(他們大寫的None讓我印象深刻),那我覺得Elixir就是有Ruby風(fēng)格的函數(shù)式編程語言了。不過即便它語法風(fēng)格設(shè)計(jì)方面跟Ruby很像,但Elixir就是Elixir,相信你會從中感受到與Ruby不一樣的編程體驗(yàn)。

Happy Coding and Writing!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容