Elixir 与 Go 对比 [翻译]


声明:本文转载自https://my.oschina.net/nicozhang/blog/1645056,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

阅读时间: 16 分钟

之前一直做 Python 和 Rails(暂时代替 Ruby) 的开发。 这两种语言都有自己的优势,应用都很广泛,但也不乏有缺陷,最突出的问题莫过于性能了。所以,在高并发到来之前,需要寻找高性能的语言来做替补,主要用来替代 Rails 的角色。Golang 是首先想到的,但在 Rails 社区 Elixir可以说高性能的代名词。在查找这两门语言对比的时候,发现了这篇文章。原文链接


在过去的几年里, Elixir 和 Go 的流行度均有巨大的提高。这两种语言通常能满足开发人员寻找的高并发方案。它们遵循许多相似的原则,但两者都对一些特殊场景下的应用做了折中。

下面通过它们的背景、编程风格及并发的处理对比这两门语言。

起源

Go/Golang自2009年起由Google研发,以二进制文件( 编译后)的形式运行在部署的平台上。 最开始的时候,它被作为一种尝试去 创建一种新的编程语言, 以解决其他编程语言的主要弊端,并保持其优势。

Go在实现开发速度、并发性、性能、稳定性、可移植性和可维护性的平衡方面做得非常出色。因此,Docker和InfluxDB都是用Go构建的,包括谷歌、Netflix、Uber、Dropbox、SendGrid和SoundCloud在内的许多大公司都在使用它来做分类工具。

Elixir自2011年由Jose Valim在Plataformatec的时候开发的,运行在BEAM VM, 也就是 Erlang VM。

Erlang自1986由爱立信开发,用于高可用分布式电话系统。它已经扩展到诸如网络服务器等许多其他领域,并已经实现了9个9的可用性(31毫秒/年的停机时间)。

Elixir的设计目标是在保持与Erlang生态系统兼容性的同时,还能够在Erlang VM中实现更高的可扩展性和生产力。 它通过在Elixir代码中使用Erlang库来实现这个目标,反之亦然。

为了避免重复,我们将Elixir / Erlang / BEAM统称为“Elixir”。

许多公司已经在生产环境中使用 Elixir, 这其中包括 Discord 、 Bleacher Report 、 Teachers Pay Teachers、 Puppet Labs 、Seneca Systems 和 FarmBot。 以及其他很多项目也是使用Erlang构建的,包括WhatsApp,Facebook的聊天服务,Amazon的CloudFront CDN,Incapsula,Heroku的路由和日志记录层,CouchDB,Riak,RabbitMQ以及全球约一半的电话系统。

编程风格

理解每个运行时的核心原理,才能对 Elixir 和 Go 做可靠对比,因为这些构建块是语言的基础。

Go是一种对传统C系编程背景的人来说更容易熟悉的语言, 尽管它做了一些有利于函数式编程的设计。Go的静态类型,指针和结构体,会让你有种似曾相识的感觉。

函数可以附加到结构体类型,这种组合方式随着时间的推移更能促进项目的增长。函数可以在任何地方创建并附加到结构体,而不是将其嵌入到必须扩展的对象中。

如果一个方法需要被多种类型的结构体调用时,可以为这个方法定义一个接口以便于提供更大的灵活性。典型的面向对象的编程语言必须首先定义一个对象来实现一个特定的接口, 与之不同的是Go中的接口将被自动应用于与之相匹配的任何事物。Go的接口实例

Elixir 更倾向于函数式风格, 但融合了一些面向对象语言的原理,使它的这种过渡不显得那么违和。

变量是不可变的,由于使用消息传递,就不需要传递指针,这意味着函数调用是非常直接的。 传入参数,返回结果,没有其他影响。 这简化了一些例如测试和代码可读性方面的问题。

由于数据不可变,诸如for循环之类的常见操作是不可用的,因为无法创建一个递增的计数器。尽管Enum(枚举)库以一种简单的方式提供了常见的迭代模式,但递归通常被用来代替这类操作。

由于递归使用频繁,因此Elixir还专门做了尾部调用优化。如果函数的最后一次调用的是自己,则可以防止调用堆栈的增长,从而避免堆栈溢出错误。

Elixir广泛的采用模式匹配,这与Go利用接口的方式非常相似。 使用Elixir,函数可以定义为:

def example_pattern(%{ "data" => %{ "nifty" => "bob", "other_thing" => other}}) do   IO.puts(other) end 

使用 map 模式作为函数的参数,只有当传入的 map 包含键为 data, 值为嵌套的 map, 且值中又包含键为 nift 值为 bob 和 另外一个键为 other_thing的时候,改函数才会被调用。而此时,变量other才会被赋值。

从函数参数到变量赋值,尤其是递归,都会用到模式匹配。这里有些例子,可以感受一下。 结构可以被定义为类型,然后也可以在模式匹配中使用。

从原理上讲,这两种方法是非常相似的。两者都将数据结构和数据操作分开。同时也都是通过匹配来定义函数的调用,Go 通过接口,而 Elixir 通过模式匹配。

尽管 Go 允许函数通过特定的类型调用, g.area(), 但实际上这跟 area(g)是一样的。两者之间的唯一区别,在Elixir中 area()需要返回结果,而 Go 则是在内存里实现了一次引用。

由于采用这种方法,这两种语言的组合性变得非常强,在项目的整个生命周期中,不用去控制、扩展、注入以及重建大的继承树。这对大项目来说是个重大的利好。

此处最大的区别是,使用 Go 的时候,为了重用这种模式会在函数外定义,但如果组织不好,可能导致创建大量重复的接口。Elixir 不能简单的复用模式,但模式总是在使用它的地方定义。

Elixir使用"strong" typing(强类型)而不是static typing(静态类型), 而且大部分是推断出来的。在 Elixir 中, 没有符号重载,所以不能用+连接两个字符创会让你感觉很困惑。在 Elixir 中,<>可以用来连接字符串。

如果不理解背后的原因,这会让你感觉憋得慌。编译器能够通过显式的运算符推断出加号两边必须都是数字。同样,<>任何一边都必须是字符串。

强类型本质上是指动态类型, 其中编译器通过透析器(dialyzer)可以捕捉每种类型, 模式匹配中歧义参数除外(避免为_ 指代的不会被用到的变量或参数分配内存,就像Go一样,只是用来占位) 。 代码注释可以用来定义这些异常情况下的类型。 这样做的好处是,你可以获得静态类型的大部分优势,又不会失去动态类型所带来的灵活性和元编程特权。

Elixir的文件可以使用.ex作为编译代码的后缀,也可以用.exs 作为运行时编译的脚本后缀,例如 shell 脚本等。Go 总是被编译的, 然而 Go 的编译器是如此之快,以至于庞大的代码块也能在瞬间完成编译。

并发

并发性是本次比较的关键。现在你已经对语言风格有了一个基本的了解,其余部分会更有意义。

传统意义上,线程所涉及的并发是比较重量的。最近,一些编程语言开始使用"轻线程"或"绿线程", 实际上它是在单个线程里使用一个调度器来管理不同逻辑的轮流执行。

这类的并发模型是内存高效的,但依赖于运行时指定的执行流程。JavaScript 已经使用这种风格很多年了。举个简单的

本文发表于2018年03月18日 22:38
(c)注:本文转载自https://my.oschina.net/nicozhang/blog/1645056,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2048 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1