【翻译】关于Go和Erlang的一些看法

原文在此,本翻译仅供学习与参考,如有侵权请联系我,我会迅速删除。

更新:我发现我这篇文章的观点写得不是很明确。我没有说Go语言多么不好或者说它应当改得更像Erlang。我想说的是,Go语言选择的语言特性使它在对高可用性、高并发低延迟有要求的服务器端应用场景下无法代替Erlang。同时请注意,我没有写有关类似的Julia语言的内容。我听说Go语言被广泛宣传为——不仅仅是应用于新的项目,而且是在重写旧项目时——可取代Erlang的选择。没有人会用这个口号来宣传Julia,但是Go和Node.js往往像这样被某些人宣传为一个更合适的选择。另外,我也没有说Erlang是万金油!这篇文章仅仅针对某些适合Erlang而不适合Go的场景来说的。

我尽量在讲Go的缺点时刨除我的主观情感因素,比如语法的问题或是模式匹配的缺失,同时讲解Go的语言特点与运行时不适合某些系统的客观原因。我会从Go好的方面开始入手讲解。

Go的闪光点

客户端

就像Rob Pike所写的那样,Go语言给他最大的惊奇就是,Go语言竟然吸引了一帮Python和Ruby开发者而不是C++。我认为这个趋势很正常。不会有比pip或gems安装更慢的客户端了!(虽然由于某些原因用于客户端的Node.js也在增加,wtf Keybase?)
(译者注:上面这段我没太看懂,欢迎纠正……)
Go语言为开发者提供了更快捷方便的使用带有垃圾回收机制和并发原语的高阶静态类型语言的机会。对C++开发者来说转向Go开发也棒极了,在我的机器上,频繁崩溃是那些热衷于错误使用内存的C++程序——比如Hipchat和Spotify的专属权利。但是Rob Pike指出,C++开发者们并不想要那个简洁而又强大的Go语言世界。Ruby和Python开发者都很明智,与C++开发者正相反。

工具

构建并运行依赖于第三方库的程序是很容易的,完全用不着第三方工具,Go自己已经提供了这样的工具。虽然Go自带的工具还不太完善,仍然有像Godep这样的东西来弥补其不足,这也足够压倒其它语言了。

Go的不足之处

在写一些低延迟的容错系统时,有一些Go的语言设计是有害的。

并发

是的,在第一段中我将“并发原语”列为加分项。这是对于取代Python、Ruby和C++在客户端编程方面考虑的。但是在需要容错的复杂后端编程时,Go和其他共享状态的语言一样糟糕。(译者注:这么说来只有函数式语言可以考虑?)

抢占式调度

其实这方面Go做的还不错。Go的抢占式调度是用系统调用完成的,但是现在当goroutine检查系统栈——每次调用函数时都要发生——时抢占会发生,与此同时,如果goroutine运行的时间比某个时间段长,可能就会发生错误而导致抢占。(译者注:前面这句我没太理解……)虽然这是个进步,但是比起Erlang的reduction counting机制和用于提高与C的集成度而新增的脏调度器,Go差的还远。

垃圾回收

Go的垃圾回收使用全局的标记清理器。在清理的过程中,所有的goroutine都会被暂停,糟糕的是这会导致延迟。重复一遍,低延迟很难,运行时做的越好越能降低难度。

错误处理

这段不仅仅是吐槽Go没有异常的设计和通过检查函数的第二个返回值是否为nil来检查错误的。Goroutines没有特定的身份标识,这意味着Go中没办法链接或监视goroutines。没有链接(取而代之的是使用panic和defer),没有进程隔离,这意味着你没办法在崩溃时恢复或者以稳定的状态重启。在生产环境中这会产生很多bug,它们中的许多会成为Heisenbugs,所以说进程的分层和隔离、基于独立性的链接是容错的关键。
Go使用nil来弥补对错误处理的忽视。在2014年这被认为是可以接受的,但是我对这个不放心。我保留我的意见,我对Go的前景仍然感到不明朗。

自省机制

没有交互式解释器(REPL)会对开发工作造成困扰,没有远程命令行对于一个运行系统来说是个大问题。Erlang有一个令人印象深刻的追踪机制和在这些机制上建立的工具比如recon_trace。Erlang的自省机制极大地提高了开发工作的效率,比如对一个复杂的运行中的系统的维护工作。

静态链接

是的,这个特性看起来像是优点,但在长期运行的系统中会成为缺点。虽然没有静态链接会导致运行速度的降低,但是这使得Erlang拥有了热代码替换的优点。还有重要的一点是,Erlang优秀的的调度和垃圾回收机制,使得这样的速度取舍并不会使基于Erlang的实现真的比其他语言的实现更慢,更何况Erlang的实现是唯一一个能进行热代码替换的。

代码组织

OTP框架为许多常见的模式提供了库。使用OTP不仅意味着可以少写代码、可以更好地抽象,还意味着可以提高代码可读性。依照OTP的标准来写应用代码,supervisor和worker(genserver, genfsm, gen_event)意味着一个新加入的开发者也可以看懂进程树以及进程之间是如何通讯的。Go语言的channel、没有身份标识的goroutine和将goroutine分隔成若干模块的机制的缺乏,使得Go语言的代码更难以被读懂。

Go语言可以(或者说应该)进行改进吗?

Erlang已经发展了十数年而Go语言目前还是一门新生的语言,所以Go语言可以在上述方面进行改进吗?某些方面可以,但是大部分不行,因为在设计Go语言时选择舍弃了容错和低延迟的机制。
在这里我并不是说Go语言“不好”或者是“错误的”。Go语言只是在设计中做出了不同的选择,所以它在解决另外一些问题时比Erlang这样的语言更适合。

译后注:译者并未完全理解本文,所以翻译错误在所难免,欢迎读者纠正。