概述
Elixir 是一種基於 Erlang 虛擬機的函數式,面向並行的通用語言, 它是一門通用語言,因此不只能夠用在擅長的高可用,高併發場景下,也能夠用在 web 開發等場景下。 Erlang 誕生於 1986 年,愛立信。javascript
有了 Erlang,爲何還要 Elixir? Erlang 畢竟誕生的早,雖然有不少優秀的特性,可是語法很是晦澀難懂,甚至沒有支持 String Elixir 只是 Erlang 很簡單的封裝,不只保留了 Erlang 全部的優秀特性,還提供了相似 Ruby 那樣高效的語法。html
和 Erlang 相比,Elixir 的語法有 2 個明顯的優點java
- macro: 能夠簡化不少的代碼
- pipeline: |> 能夠省卻不少臨時變量的定義
環境搭建
官方安裝文檔:https://elixir-lang.org/install.html 建議在 Unix-like 的系統下經過編譯源碼的方式安裝,可以及時體驗最新的特性web
安裝 Elixir 以前要先安裝 Erlangruby
語言基礎
在 Elixir 中,任何東西均可以理解爲 *表達式+返回值*。 Elixir 中,任何數據不可改變,變量的賦值本質是把變量指向了其餘的內存地址,不改變變量原來內存地址中的內容, 因此,等於(=)在 Elixir 中實際上是 綁定(binding) 的意思,把右邊的值綁定到左邊的變量上,並返回右邊的值網絡
類型
Elixir 中的類型主要有如下幾類:併發
Number
Elixir 中整數和小數統稱爲數值類型,整數的除法和求餘用 div 和 rem 宏來實現函數
Atom
原子存在原子表中,不會被 GC 自動回收 true/false 在 Elixir 中是原子 :true/:false nil 在 Elixir 中是原子 :nil高併發
Tuple
元素個數是固定的,適用於小的集合 經過 put_elem 修改 tuple 以後,其實返回的是一個新的 tuple,原來的 tuple 並無被修改性能
List
list 能夠表示爲 [head|tail] 在 list 的末尾追加元素會致使 copy 整個 list,因此通常都在頭部添加元素
Map
若是 key 是原子 ex. %{a: "xx", b: "yy"}; 不然 %{1 => "xx", "a string key" => "yy"}
Binary and Bitstring
長度是 8 的倍數的 Bitstring 也是 Binary
String
Elixir 中的 string 本質就是 Binary
Function
function 在 Elixir 中是一等公民,因此它也能夠存放在變量中
Reference
BEAM 實例的引用
pid
Erlang process 的惟一標識
Keywork List
一種 list,每一個元素都是一個包含 2 個元素的 tuple
IO List
一種深度自包含的結構,能夠用來高效的在處理 IO 和網絡數據 ex. 下面這個例子,文件寫入時會分紅 3 次,由於 a,b,c 的內存地址是不連續的 若是把 a,b,c 拼成一個字符串再寫入文件的話,新的字符串會再次分配一次 a,b,c 所佔用的內存量
{:ok, file} = :file.open("/tmp/tmp.txt", [:write, :raw]) a = "aaa" b = "bbb" c = "ccc" output = [a, b, c] :file.write(output)
用 io_list 能夠避免上面的問題
{:ok, file} = :file.open("/tmp/tmp.txt", [:write, :raw]) a = "aaa" b = "bbb" c = "ccc" output = [a, [b, [c]]] :file.write(output)
IO.puts 輸出時會拍平 io_list
控制流
通常語言中的流程控制就是判斷(if, case…),循環(for, while…) Elixir 中雖然也有 if,case(經過 macro 來實現),可是儘可能不要使用
Elixir 中經過模式匹配來實現判斷,經過遞歸來實現循環
模式匹配的例子
經過不一樣的函數,實現變量的類型判斷
defmodule ElixirIntro do def judge(x) when is_integer(x) do IO.puts("#{x} is integer") end def judge(x) when is_bitstring(x) do IO.puts("#{x} is string") end def judge(x) do IO.puts("#{x} is not integer and string") end end
遞歸的示例
遞歸是 Elixir 中經常使用的技巧,經過遞歸能夠寫出簡單易懂的代碼 下面示例是累加求和的遞歸寫法
defmodule ElixirIntro do def sum(n) when n <= 1 do n end def sum(n) do n + sum(n - 1) end end
上面的遞歸寫法雖然能完成功能,可是運行過程當中消耗的內存很比較大,這種寫法就是遞歸被人詬病的地方。
在 Elixir 中,咱們應該使用尾遞歸的方式來完成循環,由於尾遞歸會被優化,只佔用固定數量的內存,上面的示例以下:
defmodule ElixirIntro do def sum(total, n) when n <= 1 do total + n end def sum(total, n) do sum(total + n, n - 1) end end
所謂尾遞歸,就是函數在最後只調用了本身
代碼組織(模塊和函數)
Elixir 的代碼用 mix 來管理,經過 mix 建立,管理,發佈工程。 代碼主要是 module 和 function
$ mix new hello $ cd hello $ iex -S mix
Elixir 工程的代碼都在 lib 文件夾下。
錯誤處理
錯誤處理是 Elixir 中的一級概念。
通常的錯誤處理思路都是儘量的捕獲錯誤(可預期和不可預期的):
- 可預期的錯誤直接處理
- 不可預期的錯誤捕獲不到可能系統崩潰,捕獲後通常也是直接跳過
Elixir 的核心能力之一是應對高併發,因此它的錯誤處理思路也不同。 Elixir 錯誤處理的目的 不是下降錯誤的數量 ,而是 下降錯誤帶來的影響 而且可以從錯誤中 自動恢復 。 因此,Elixir 的處理方式:
- 可預期的錯誤,和傳統方式同樣,儘量處理
- 不可預期的錯誤,直接崩潰重啓。會致使崩潰的地方儘快崩潰
Elixir 的觀點:
- 有些錯誤發生後,要想解決很困難,在發生錯誤後接着處理可能會致使更嚴重的錯誤
- 不少運行時的錯誤,極難重現,大部分都能經過重啓來解決
總結
Elixir 雖然在它的領域有先天的優點,可是確定也有不足之處:
- speed: 性能不是 Erlang 平臺的優點,畢竟有一層 BEAM 虛擬機, 高併發 != 高性能
- ecosystem: Erlang/Elixir 的生態遠沒有其餘語言成熟(好比 java, javascript, ruby 等)
Elixir 不是萬能的,可是學習 Elixir 能夠打開你的視野,特別是對於一直使用面嚮對象語言的同窗, 學習 Elixir 可讓你以另一種視角看待程序設計。