在京东上搜Elixir,会发现Elixir是一种琴弦的品牌,然而我今天想讲的Elixir是一种编程语言。它的Logo如下
早就有学习这门语言的打算,只是事情太多,忙着忙着就忘了。公司最近的技术分享中,明哥分享了一个关于Phoenix(基于Elixir的Web开发框架)跟Rails的比较的话题,重新燃起了我对这门语言的好奇心。接下来让我们一起简单的来看看Elixir这门语言。
1. Elixir是什么
我们先来看看官方的解释
Elixir is a dynamic, functional language designed for building scalable and maintainable applications.
Elixir 是一门动态的函数式编程语言,主要是用来构建可扩展,可维护的应用程序。好吧,把函数式去掉的话Ruby也可以做到。Ruby是一门动态的命令式编程语言,可以用来构建可扩展及可维护的应用程序。不过Ruby设计的初衷并不是为了解决什么问题,它的初衷是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运行在Erlang虚拟机上,以低负载,分布式和容错系统而闻名。已经成功应用在Web开发领域,以及嵌入式软件领域。这也算是编程语言的另一个发展方向吧,除了直接用系统级编程语言(如C,C++,Go)来开发编程语言,我们还可以在比较成熟的虚拟系统上构造我们想要的编程语言。比如JavaVM之上有人发明了Clojure,Scale这些函数式编程语言(身边的人好像比较喜欢Clojure),而有位Ruby英雄在ErlangVM上实现了Elixir。
2. 安装Elixir
安装教程没有能比官方文档更加详细的了,我这里就不重复说了,我就说说我在Mac上安装的时候遇到的比较尴尬的问题吧。
Elixir是运行在Erlang环境下的编程语言,当我们用HomeBrew安装Elixir的时候,它也会顺势帮你安装Erlang。
想想很久之前我们用HomeBrew安装了Elixir,以及配套的Erlang环境。然后我们卸载了Elixir,却留下了Erlang。几个月后我们重新安装Elixir这个时候,也配套安装了最新版本的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>
我们的系统同时保留着两个版本的Erlang,以及新版本的Elixir。而系统还默认引用这旧版本的Erlang,这表明了我们会把新版本的Elixir运行在旧版本的Erlang上面,你可能就会得到如下错误。
> 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: []
这是个错误示范,可能你也会遇到类似的问题,报错信息不会完全一样,不过应该也差不多。究其原因就如上面所说Eixir版本跟Erlang版本不兼容,当我们想在老版本的Erlang上跑新版本的Elixir就出问题了。
解决方法有两个
1) 切换Erlang的默认版本,换成我们期待的最新版本的Erlang
我们通过HomeBrew的命令
brew switch erlang 20.1.3
只需要简单地把默认Erlang运行环境指定到我们需要的最新版本的,然后重新运行Elixir就能够进入我们期待的REPL环境了。
> 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的话我建议可以把他们都移除掉,毕竟一个Erlang包会占用掉系统200多MB的磁盘空间。
通过brew
命令的uninstall
指令,加上--force
标识就能够移除所有版本的Erlang
brew uninstall --force erlang
如果单独移除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,当我们只想单独卸载被依赖的Erlang的时候就会有这个警告,我不建议用它的命令保留Elixir而单独移除Erlang,毕竟该清理的还是要清理干净,重新安装也耗费不了多少时间。我们只需要依次运行以下命令
brew uninstall elixir
brew uninstall --force erlang
brew install elixir
把Elixir以及所有版本的Erlang删除之后,再重新安装Elixir,我们就可以体验最新版本的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了吧,来简单写一个脚本。
(1) 编译模式
写习惯了解释性语言,已经很久没试过编译代码了。想想以前写C的时候运行之前都是需要先编译,然后执行生成的二进制文件。现在我们来看看Elixir是如何操作的。
Elixir有个约定,需要编译的文件以.ex
后缀名结尾,而直接运行的脚本则.exs
后缀名结尾,我们也依照这个约定吧(即便我们都知道在Unix操作系统里面后缀名是什么根本就无所谓)。这里我们创建一个文件math.ex
# math.ex
defmodule Math do
def sum(a, b) do
a + b
end
end
这种编码风格跟Ruby很像,实际上我们定义了一个包含sum方法的模块Math,然后运行编译命令
> elixirc math.ex
编译成功后会生成名为Elixir.Math.beam
的文件
> ls
Elixir.Math.beam math.ex
这个就是编译后的字节码文件,熟悉Java的人应该知道Java里面也有类似的存在。然后我们在当前目录下面运行iex
进行REPL环境,就能直接使用这个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环境都没有。Elixir考虑到了这一点,按照习俗我们在新目录下创建一个math.exs
的脚本文件
# math.exs
defmodule Math do
def sum(a, b) do
a + b
end
end
PS: 内容跟编译模式中的文件一样,只是后缀名不同。
> ls
math.exs
然后我们可以直接用相关的命令运行对应脚本
> elixir math.exs
由于我们math.exs
里面只是定义了一个简单的模块和方法,并没有更多的逻辑,所以这次执行也看不出什么效果。不过在Elixir机制中我们可以像下面这样操作,直接地把目标文件加载到REPL环境中
> 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
可以看到运行结果跟之前编译模式一样的只是少了手动编译那一步,这有利于我们对源码文件进行简单地测试,而不需要每次测试的时候都手动编译一次。
官网的建议是平时开发业务逻辑代码都以
.ex
后缀名结尾,最后需要被编译成字节码(这可能是性能方面考虑吧)。而编写日常单元测试或者配置信息则以.exs
后缀名来结尾。
4. 总结
对这门语言的学习,大概只是看了4-5天的入门指南。它是否真如文档所说能够用来构建可扩展性高的系统,以及它是否真的有传闻说的那样地高性能呢?这一点还有待考证。
不过经过这两天的学习,我觉得这是一门挺值得入手的语言,特别是对于Ruby系的朋友来说。如果说Haskell是有Python风格的纯函数式编程语言(他们大写的None让我印象深刻),那我觉得Elixir就是有Ruby风格的函数式编程语言了。不过即便它语法风格设计方面跟Ruby很像,但Elixir就是Elixir,相信你会从中感受到与Ruby不一样的编程体验。