概述
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 可让你以另一种视角看待程序设计。