Elixir 即将放弃对 Erlang/OTP 21 以下版本的支持

前言


维持了两年 Open 状态的 Issue (#6611) 就在最近几天被解决了,Elixir 至此正式支持 Erlang/OTP 21 版本的更多剩余新功能。

因为某些标准库实现或特性的增强使用了 Erlang 的新功能,这意味着最低 OTP 支持版本被迫升高。来自 José Valim 本人公布的计划,是 2020 年 1 月的 1.10 版本发布之时。虽然当前还在继续更新 1.9,但时间也所剩无几。

详细介绍


在 OTP 21 中新增了两个核心 API: is_map_key/2map_get/2。并且它们都可用作 guards 测试。它们带来的意义是,在 Elixir 中终于能通过 guards 条件来过滤结构参数的类型,而不再是以往那样在参数中添加模式进行匹配。

基于模式将结构类型作为函数子句的条件:

def serve_drinks(%User{age: age} = user) when age >= 21 do
  # ...
end

实际上我想说明的只有参数中的模式匹配,跟 guards 条件无关。但这是 José 本人的例子,我觉得挺好,就拿上来了。通过 %User{} = user 模式,起到限定结构类型的作用,以往都是这么做的。

但因为 OTP 21 的新 API 支持的缘故,Elixir 可以用上更加「语义直观」的做法,也就是前面说的 guards 条件。基于 is_map_key/2map_get/2 可以组合出新的 guards,也就是计划新增的 is_struct/2(注:后来被划掉了)。它的实现就是对前两者的组合:

defguard is_struct(term, struct)
         when is_map(term) and
                :erlang.is_map_key(:__struct__, term) and
                :erlang.map_get(:__struct__, term) == struct

嗯…… 我们知道 Struct 实际上就是多了某些元数据并限制字段类型为 Atom 的 Map。通过 OTP 新增的两个 guards 能恰到好处的实现 is_struct/2 这个新的 guards,而不用再像从前那样在参数中添加模式:

def serve_drinks(user) when is_struct(user, User) do
  # ...
end

是的,上面的代码更加语义化,更加直观了。增强了「条件」这个概念,也就是结构类型的限定。

细心回味会发现,模式并非完全可替代。例如第一段代码中,模式有解构的作用,在 guards 中使用了解构出来的变量。这 is_struct/2 是做不了的。的确是这样,但 is_struct/2 仅仅是一个例子,真正的核心亮点还是在于 OTP 21 新增的两个 guards。要限定结构并将结构内部的值拿出来进一步作为条件,实际上也只是多了一些 guards 组合而已。

def serve_drinks(user) when is_struct(user, User) and :erlang.map_get(:age, user) >= 21 do
  # ...
end

是的,它变啰嗦了一些…… 但是 Elixir 从来就不是精简派的语言。相反,它带来了新的好处,那就是复用性。因为我们可以组合出这种包含内部值作为条件的自定义 guards。例如:

defguard is_drinking_age(user)
         when is_struct(user, User) and
                :erlang.map_get(:age, user) >= 21

这样就可以在更多的函数中复用这个 guards,而不需要进行重复的解构。所以认为更加啰嗦,实则也是片面的看法。

结束语


我对 Elixir 放弃 OTP 21 以下版本没有任何不满或负面评论,我甚至拍手叫好。从我个人的角度,我没有被低版本 OTP 依赖拖累过,我的项目要么是最新版,要么可以随时升级到最新版。从官方团队的规划看待,一个在两年前就公布的计划,这已经属于非常靠谱了。

毕竟发布那么久的 OTP 22 新功能又等要到何年何月才能适配上了,实际上我甚至觉得因为顾及历史包袱维持两年多的部分功能不能更进的状态,对很多很多人而言其实不太公平。

我曾经还遇到过因为某个依赖而死活不升级 Erlang 版本的(导致 Elixir 版本也无法升级),并且版本很低。要知道因为一个死掉的依赖库而不升级版本,其前提已经告诉你了,就是它已经死掉了。你不去解决,那么一辈子不可能再升级版本,等待你的也只有忍受一个逐渐不被支持的基础版本直到项目死亡而已。国内一些人对升级的看法实在太短浅,好在 Elixir 社区并不顾及国内生态:)