最近一段时间为了学习Elixir,不停的在翻阅中英文的书籍、文章和博客。确实学到不少,不过也有些东西确实让我迷惑了一阵子,比如《Elixir程序设计》一书的第8章,着实吓了我一跳,「字典:散列表、散列字典、关键字列表、集合与结构体」,这么多的数据结构,看起来都差不多,一讲到关键部分,书里说的就不甚明了,隔靴搔痒。更诡异的是,还有一些教程,压根就没提到散列字典,还有的文章,甚至出现了MapSet这种东西。

怎么回事呢。

作为一门年轻的语言,Elixir在不停的进化,随着版本升级,语言特性也在不断的更新。所以,包括《Elixir程序设计》在内的很多译作与文章,已经落后于语言的发展了。这再一次的说明了,关注CHANGELOG是多么的重要…

目前Elixir的版本是1.4.2,整理一下目前我关注到的一些被移除的模块

  • Behaviour:无疑是一个重量级的移除,使用更优雅的模块属性@callback@macrocallback来代替之前的写法,这个新特性自1.4.0开始。

  • Dict:在被移除之前,Dict似乎是实现了某种Behaviour,既可以操作keyword list,也可以操作Map,这个模块确实让人摸不着头脑,所以它在1.3.0开始被抛弃了。

  • HashDict:文档上说用Map来代替它,为啥会这样我也不是很懂,不过,这个性能测试也许能说明点什么

  • SetHashSet:从1.2.0开始,就被“软性移除”(使用它不会报warnning)了,Set模块在1.4.0时被正式放弃。文档里建议使用MapSet来代替它们。

总结一下,目前Elixir中的几种常用数据结构和操作它们的模块如下:

数据结构 操作模块 被移除的模块
Tuple Tuple
Keyword list Keyword Dict
(Linked) List List
Map Map Dict, HashDict
Set MapSet Set, HashSet

需要注意的是,并不是只有上表中列出来模块才能操作对应的数据结构,比如Enum模块能够同时操作Keyword listListMap。原因是这三种数据结构都实现了Enumerable协议(protocol),而Enum模块可以对所有实现了该协议的类型进行操作。协议是Elixir实现多态的手段,感觉和Clojure中同名的协议非常接近,也类似于Java中的Interface

除了移除模块,Elixir还加入了一些新的特性,我目前知道的用起来比较爽的有:

  • with...else语句,这种写法可以大大减少代码量,下面是官方文档中在with语句中使用Guard的例子,

    1
    2
    3
    4
    iex> users = %{"melany" => "guest", "bob" => :admin}
    iex> with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob"),
    ...> do: {:ok, to_string(role)}
    {:ok, "admin"}
  • ExUnit.Case里增加了describe/2宏,可以理解成把test case分组,使单元测试的代码可以更便于阅读。下面同样是官方文档中的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    defmodule UserManagementTest do
    use ExUnit.Case, async: true
    describe "when user is logged in and is an admin" do
    setup [:log_user_in, :set_type_to_admin]
    test ...
    end
    describe "when user is logged in and is a manager" do
    setup [:log_user_in, :set_type_to_manager]
    test ...
    end
    defp log_user_in(context) do
    # ...
    end
    end

    describe包裹的代码块可以单独运行测试,像这样:

    1
    mix test --only describe:"when user is logged in and is an admin"