神译局是36氪旗下编译团队,关注科技、商业、职场、生活等领域,重点介绍国外的新技术、新观点、新风向。
编者按:编程语言哪种好?这可能是许多学习编程人员甚至是外行人员都会面对的头疼问题。网络上普遍的编程语言介绍,大多都是东拼西凑的内容,并且无法让人真正认识和了解各种语言的优缺点。这篇文章,原标题是These Modern Programming Languages Will Make You Suffer,作者Ilya Suzdalnitski在文章中针对15种编程语言展开了详细测评,希望对你有所帮助。
图片来源:deepu
懒人目录
概述篇:编程语言最重要的特征
一星篇:C++,JAVA
二星篇:C#,Python,Rust,TypeScript
三星篇(上):Go,JavaScript
三星篇(下):Haskell,OCaml,Scala
四星篇:Elm,F#
五星篇:ReasonML,Elixir
在多核处理器和大型代码库时代,Go就是设计出来提高编程生产力的。Go语言的设计者们十分讨厌当时在谷歌被广泛使用的C++,这是他们设计Go的主要动力来源。
所属的编程语系: C
并发性是Go语言的杀手级特征,它一开始就是为了并发性所设计的。就像Erlang/Elixir一样,Go语言也遵循关于mailbox的并发模型。
不幸的是,Go的“线程”(goroutine)不提供Erlang/Elixir进程一样的容错性。换句话说,goroutine中的异常会使得整个程序崩溃,而Elixir进程中的异常只会让那个进程崩溃。
谷歌创建出Go的主要原因之一是,它有很好的编译速度。当时还有个笑话,称谷歌在等待C++代码编译好的时间内就创建出了Go。
Go是一门速度很快的语言,Go程序的启动速度非常快。由于它会编译出本机代码,所以它的运行时速度也非常快。
Go是一门很简单的语言,如果一个人之前有其他编程经验,他只需要花上一个月就能学好Go。
Go不支持异常。不过,Go让开发者们能够显式地处理潜在错误。和Rust类似,Go会返回两个值——调用返回的结果,以及一个潜在的错误。如果整个过程没什么问题,错误的返回值会是nil:
result, err := someFunction( args... );
if err != nil {
// handle error
} else {
// handle success
}
也许有人会不同意,但是我个人认为,缺乏OOP特性是一个很大的优点。
在此,我想再次引述Linus之父林纳斯·托瓦兹(Linus Torvalds)的话:
C++是一门恐怖的面向对象语言……如果你只能用C来完成项目,那么你就不会因为任何愚蠢的对象模型把事情搞砸。
托瓦兹因为他对C++和OOP的公开批评而闻名于世,他百分百正确的一件事,就是程序员们的选择的确受限。事实上,程序员们拥有的选择越少,他们的代码弹性就越好。
在我看来,Go有意省去了很多OOP特性,从而避免和C++犯下一样的错误。
有些标准库非常愚蠢,其中大部分都和Go返回超出界限错误的理念不符(比如,那些库会为一个指针返回-1这样的值,但Go想要的返回值是(int, error)这样的),而其他的库则依赖于全局状态,比如flag和net/http。
Go的标准库缺乏标准化。比如,有些库在错误时会返回(int, error),有些库会返回-1这样的值,而其余的库则是依赖全局状态(比如flag)进行错误处理。
Go的生态系统并没有JavaScript那么大。
阿伦·凯创造面向对象编程时,C++根本不是他心目中完美的面向对象语言。就像“剪切复制粘贴”的创建者拉里·特斯勒(Larry Tesler)一样,他引入复制和粘贴时,并没有想到要做出Go这样的语言。
——推特用户Jasper Van der Jeugt @jaspervdj
有相当多的现代编程语言都有一种或多种形式的泛型(包括C#和Java,甚至C++都有template),泛型可以让开发者针对不同类型重用函数实现。
没有泛型,我们就必须为整数、双精度浮点数、浮点数各自实现add函数,这会导致许多重复代码的出现。换句话说,因为Go缺乏泛型,所以Go里面有大量的重复代码。就如一些人所说,“Go(有“去”的意思)”是“去写一些样板”的缩写。
不幸的是,当安全的替代方案已经存在了几十年的时候,Go却纳入了空值。
Go没有为不可变性数据结构提供内置支持。
Go是一门不好也不坏的语言,我们必须小心使用不好的编程语言,否则,我们也许会在接下来20年中,都必须一直使用它们,并且毫无进步。
——Will Yager
如果你不是在谷歌工作,也不用处理类似谷歌的用例,那么Go可能不是最佳的语言选择。
Go是一门最适合系统编程的简单语言,但对于API开发而言,它并不是一个好的选择,因为现在有很多更好的可选语言(后文中将进一步说明)。
尽管Go的类型系统更弱,但总的来说,我认为Go是比Rust更好的编程语言。Go是一门简单的、速度很快的语言,很容易学会,也有很好的并发性特征。所以,Go是一门成功的C++进阶版语言。
Go荣获“最佳系统语言奖”。毫无疑问,Go是系统编程的完美选择。Go是一门低级语言,用Go建造的许多成功项目(比如Kubernetes, Docker和Terraform),都可以证明其十分适合这个领域。
作为世界上最流行的编程语言之一,我不需要再对JavaScript做介绍。
当然,这个排名绝对没错,JavaScript应该比Rust,TypeScript和Go排名高,让我们来看看为什么。
所属的编程语系: C
JavaScript最大的优点就是它的大生态系统。JS能够在你能想到的任何地方用到——前端和后端web开发,CLI编程,数据科学,甚至机器学习。JavaScript可能有一个支持一切的库。
JavaScript和Python一样,是最容易学习的编程语言之一,新手花上几周就能掌握JS。
像Python一样,JavaScript是动态类型的,这没什么好说的。JS的类型系统有时会非常奇怪:
[] == ![] // -> true
NaN === NaN; // -> false
[] == '' // -> true
[] == 0 // -> true
就像在TypeScript测评部分所提到的一样,拓展运算符对于性能来说是很糟糕的,并且在拷贝对象时不能实现深度拷贝。虽然JS有能够帮忙处理不可变性数据结构的库(Ramda/Immutable.js),但是它缺乏不可变性数据结构的内置支持。
当在JavaScript中使用React时,我们必须使用PropType。然而,这也意味着,我们必须维护PropType,这就非常可怕了。
同时,如果不够仔细的话,可能还会引入不易察觉的性能问题:
<HugeList options=[] />
这样看起来无害的代码可能会造成性能噩梦,因为在JavaScript中,[] != [] 。即使options值没有改变,上述代码也会导致HugeList在每次更新时都重新呈现。这种问题还会恶化,直到用户界面最终无法使用为止。
JavaScript最大的缺点可能是关键字this。this的行为是不一致的,需要关注其细节内容,这意味着在不同的上下文中,this代表的内容可能不一样。this的行为取决于调用已有函数的对象。用关键字this通常会导致一些不易察觉并且难以调试的奇怪错误。
JavaScript支持使用事件循环的单线程并发,这样就不需要线程同步机制了(比如锁机制locking)。虽然JS最开始创建时没有考虑并发性,但是和其他大部分语言相比,JS使用并发代码要容易得多。
JavaScript比TypeScript更早得到一些新的冷门的特性支持。使用Babel编译JS程序时,甚至能够启动它的一些实验特性。
捕获错误或抛出错误是优先考虑的错误处理机制。
JavaScript不是一门设计得很好的编程语言,它最初的版本是在十天之内创造完成的(虽然在后续版本中,已经解决了许多缺点)。
虽然JS有很多短板,但它对于web全栈开发而言,的确是一个不错的选择。在适当的规范和检查下,JS可以是一门好的编程语言。
延伸阅读:
现代编程语言终极测评:概述篇
现代编程语言终极测评:一星篇
现代编程语言终极测评:二星篇
现代编程语言终极测评:三星篇(下)
现代编程语言终极测评:四星篇
现代编程语言终极测评:五星篇
译者:俊一