Elixir 簡介

概述

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 可讓你以另一種視角看待程序設計。

相關文章
相關標籤/搜索