《架构师》2019年6月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

一张主流编程语言的变迁图,讲清程序员迁移模式

作者 apenwarr 译者 方彦

我绘制了一个主流编程语言的变迁图,用以表示程序员在不同语言之间的切换路径。

关于编程语言,还有很多类似的图可以表示它们相互之间的演进。不过我并不想从语言设计者角度来说明这个问题,而是想从程序员本身来看待语言演变。虽然两者间有些接近,但并不完全相同。

从该图可以看出,如果开始使用的是编程语言A,下一个最有可能切换过去的是哪种语言。这种推测不是非常科学。不过如果你需要精确的科学,就不会在这里阅读这篇文章了,对吗?也许这张流程图对我来说,能揭示更多的内容。

声明:在此处,不考虑程序员最喜欢的是什么语言。人们可以在任意两个语言之间切换,也可以学习很多种语言、然后选择最适合工作的一种语言。本文中的观点有一定的倾向性。

在接下来的篇幅里,我所阐述的均为个人观点。为了不影响读者阅读,就不再一一做出声明了。

程序员迁移模式

我想强调下最普遍的“终极节点”。在这些节点上,人们在他们所处的维度找不到更好的可替代编程语言。这些终极节点包括:Rust、 Java、Go、 Python 3、JavaScript和node.js(node.js作为一种特殊的JavaScript,在这里特别指出)。

几年前,我认为C也是一个终极节点。可能现在也还可以这样认为,因为有大量的重要项目(如OS内核)仍使用了C,而且可以认为它无可替代。不过有迹象表明C其实是可以替代的。我最喜欢的例子就是有趣的空指针。Linux内核有个编译器带来的致命弱点,即NULL值“不可能”出现,因此没有对函数进行空指针检查。C也是一团糟,其规格里有几个新编程语言所没有的致命错误。也许某天这些错误能被修复。

让我们回退几步。如果从顶部开始,根据人们进入编程的不同规格,可以看到四个主干:

●“低级”编程,包括asm和C。

●“商业型”或“学习型”编程,从BASIC开始。

●计算类/科技类编程,如Fortran, MATLAB和R。

●脚本/胶水编程,如shell和perl。

(我们也会谈到“数据库查询语言”,比如SQL。它是一枝独秀,所有替代它的尝试都以失败告终。数据库语言从上世纪六十年代开始就停滞不前了。甚至到现在,其关键字仍使用大写,因为(他们认为)这样能更好的理解代码。)

(我还忽略了HTML和CSS。他们是真正的语言,不过现在大家都开始学习这两种语言,图上无法用相应的箭头来标识。Lisp也没有考虑在内,因为它一直没有流行过,虽然有一小部分人一直希望它能流行起来。我还建议添加第五个分类,“配置编辑”)

(该图也忽略了Haskell。它可以在旁边用一个独立的框来表示,和其他语言之间没有出入的箭头,不过这没关系。Haskell是个自嘲式的大笑话,除非涉及到Monads,它不再使用I/O概念。)

不管怎么样,让我们回到上世纪九十年代。假设那时编程世界很简单,(1)初级程序员使用C, asm或者Turbo Pascal, (2)商业程序员使用VB, (3)数值计算人员使用Fortran, R或MATLAB, (4)胶水编程者使用sh或perl。

那时,编程语言就是这么严格划分的。画该图时,我才意识到这一点。显然,我们不会用perl来写操作系统内核,不会用MATLAB来写胶水程序,不会用VB来写大型矩阵相乘算法。

现在则变化很大。选择什么样的语言已经不再像过去那样明确了。

语言的变化主要是风格的变化

我们先来看树起点asm(汇编语言)。用Asm来写程序是相当困难的。不过即使到现在,它仍是写某些程序最好的方式(如电脑启动后的最初几个指令,或是中断处理的入口代码)。不管是在App Store里还是手机上的JIT里,每个编译语言最终都会将代码编译成汇编或机器语言。

基于asm,出现了两个分支:C类型分支和Pacal类型分支。(Algol出现的更早,不过此处忽略掉。宣称自己是Algol程序员的人并不多。Algol对其他语言影响很大。)

Pascal风格分支语言的特点是有"begin…end"。C风格语言的特点则是有括号。当然,C影响了很多的编程语言设计,这点在图中没有体现。因为我们现在讨论的是程序员,而不是语言设计人员。

首先来看看C。很奇怪,一旦人们开始使用C,就习惯用它来处理各种情况。不管实现优劣与否,它是为数不多的能合理实现所有四类编程问题的语言之一。这四类都有些难度(除了低级编程,它正是C擅长的领域),不过C都能搞定,速度也还可以。

如果你是个C程序员,接下来会使用那种语言呢?这取决于用它来做什么。

显然,C++是一个选择。虽然其名字与语法和C很像,但它其实和C风格迥异。除了BeOS,其他操作系统内核不会使用C++。在极具潜力的Rust使用前,操作系统基本都使用C编写。

但是商业(“大型程序”)和数值计算(“快速程序”)领域的人员喜欢C++。说喜欢不一定准确,但他们别无选择,只能使用C++。

对于胶水程序,很多人会直接从C(或C++)转到Python 2。我最近也这样做过。和怪异的perl不同,Python 2类似C语言风格,其语法更简单。C程序员很容易理解Python C模块(并可以编写新的Python模块)。从Python里调用C函数比其他语言更简单。如果在Java里调用,就需要处理非引用计数的垃圾回收问题。Python的“os”模块提供了C系统调用及该调用能工作的环境。程序员可以访问C语言中的错误码并设置相应信号处理程序。唯一的问题就是Python有些慢。不过只把它作为胶水语言,则可以不考虑Python的慢速。速度慢时,可以写C模块或调用C的库或子程序。

另外,Java面世后,很多C和C++商业软件的程序员非常快地切换到Java。C++编译时间长,头文件繁多,可移植性差,有释放后重用的错误问题。因此,虽然Java运行的很慢(和Python不同的是,Java宣称“理论上运行很快”),人们还是更愿意使用Java。

我记得有篇文章讲过,Go的设计者最开始认为Go可以和Java或C++媲美,但实际没有做到。Java就像知名酒店,或是门洛帕克(Menlo Park),一旦入住就不想离店。同时,程序员没有从C++切换到Java主要是因为:a)Java速度比C++慢,b)Java仍有垃圾回收的经典问题。

Go在之前已经切换到Python 2的胶水程序人员中流行起来。事实证明Python的慢速是其痛点所在。计算机复杂度急剧增加,Python胶水程序规模也越来越大。相较其优势,动态类型带来的麻烦更多,因此人们开始使用预编译二进制。Python 2占用很多内存,因此Go做了RAM改进,避免了从C++迁移到Go带来的问题。Go的难度和Python差不多,但它运行更快,占用RAM更少。

我们现在称Go是一种“系统”语言,因为提起胶水程序,我们更多的是想到perl和ruby,不过它们的作用是一样的。(试试告诉一个C语言内核开发者,Go是“系统”语言,看看他们的反应)Go是粘合剂,可以把各个组件组合到一起成为一个系统。

Hejlsberg因素

我们接下来看Visual Basic和Pascal分支。人们有不同的想法:明显正确的(“我为什么会使用与C或Java一样让人痛苦的语言呢?”),或明显错误的(“可视化的…Basic?开玩笑吧?”)。二十世纪八十年代和九十年代,一些人仍认为编程应该让新手可以方便使用,因此在个人电脑上预装了免费的编程语言,大部分都是BASIC。

另一方面,大学教授编程时,则避开了BASIC(“如果学生前期使用过BASIC,就不能对学生很好的进行编程授课”),也没有选择C。他们更倾向于Pascal,认为Pascal易于学习。就像Algol的学术论文里提到的一样,而且它的语法适合教学,能让每个学生都能听懂。因此就有了学术分支和个人电脑分支,不过它们有个共同点,那就是都和C不像。

基于PC(DOS)的BASIC演变为基于Windows的Visual Basic,这可能是JavaScript出现前使用最多、最受欢迎的编程语言。(现在,它仍是在Excel中使用的“宏”语言。目前有很多Excel的程序员,虽然他们并不认为自己是程序员。)

同时,Pascal也在努力往PC转。因为Turbo Pascal的出现,它变得流行起来,并一度成为最快的编译器。在速度上,Pascal的确没有夸张。甚至有一些C程序员也喜欢用Pascal,不是因为喜欢其类C的语法,而是因为它的速度很快。(Turbo C也可以,但速度还不够。它比其他C编译器都快。)(大学里,Pascal学术性越来越强,后来演变成Modula和Ada。如果不是美国军方在其高可靠系统中采用了Ada语言,这个分支早该终结了。现在我们可以忽略Ada。)

那时还有两个“商业”开发分支:BASIC和Pascal分支。Windows问世后,出现了Visual Basic。基于DOS的Turbo Pascal有点过时,基于WIndows的Turbo Pascal也并不出众。为了竞争,Turbo Pascal的设计者Anders Hejlsberg创建了Delphi。Delphi和Visual Basic一样,有可视化的编程环境,但它基于Turbo Pascal语言,也极少出现找不到或不匹配实时动态链接库的烦人问题。

Delphi很好,但它不属于Microsoft。掺杂商业因素后,局面变得有些困难。在一系列出人意料的事件之后,Hejlsberg离开了Microsoft,但仍继续C#的开发,发布了Microsoft.NET平台,并包含Visual Basic.Net(这是个很可怕的产品)。据称C#统一了两个分支。

不幸的是如前所述,VB.NET很可怕。它和Visual Basic几乎没有共同点,更像是C++的一个慢速版本,披了件有点非典型Basic的语法外衣,还带着一个更糟的UI设计工具。C#也不是Delphi。不过这几种语言都销声匿迹了,Microsoft尽力推动了这一点。(除了Microsoft Office,它到现在仍在使用最开始的Visual Basic语法,称为"Visual Basic for Applications",即VBA。比起.NET,它使用的更广泛,更受用户喜欢。)

我不清楚怎样才能叫做一名Visual Basic程序员。微软致力于让他们改用VB.NET,但大多数人并不愿意。我想在图中画一条“他们实际的选择”的箭头,不过老实说我也不知道应该指向哪里。也许他们成为了web开发者,或者编写了Excel的宏。

从现在看,如果写基于微软主推的基于.NET平台的Windows软件,是件很有趣的事。可能使用的语言都会深受Hejlsberg的影响。Hejlsberg的语言在反击之前被微软和Visual Basic所遏制,于是Hejlsberg转向写Typescript,这个留待以后讨论。

胶水语言的简要介绍

最初的胶水语言是Unix shell,它因引入“管道”概念也很著名。“管道”连接简便的工具来完成复杂的工作。

啊,就是那些日子

那些日子一去不复返,Perl就是献给它们的悼词

——Rob Pike

事实证明,设计小而简单的工具是困难的,通常我们没有足够的时间来做这个。能够让我们跳过这些轻便工具,致力于编写奇特的、能够粘着很多乱七八糟的小程序的语言变得越来越流行。(它对shell语言的缺陷,尤其是与引用和通配符扩展规则相关的缺陷并没有帮助。)

第一个是awk,它是语法和C类似的解析语言,可以用在shell的管道上。那时,在一种语言(sh)的一行中里使用另一种微语言(awk)有点奇怪,庆幸的是我们适应了,因为现在的web程序都是这样的。(我们略过csh,它是另一种与C语法不兼容的语言,存在不同的致命缺陷,可以被sh替代。)

接下来是Perl。awk没有足够多的标点符号,从而促成了Perl的产生。(好吧,这只是个玩笑。)

Perl开始到Perl 5,越来越受欢迎。现在,Perl停止改进语法,在Perl 6上倾尽全力,从零开始打造。(在图中并没有标出Perl 6,因为还没有人切换过去。)

这样的配置给在几个方向断层进行“粘合”留下了空间。如果程序员觉得Perl的语法差劲,可能会切换到Python。如果他们认为Perl的语法很神奇有效力,只需要一些调整,则可能会切换到Ruby。如果使用Perl来运行Web的CGI脚本,则可能会保持原样,也可能会转而切换到PHP。

Ruby很快成为Web服务器支持的语言(进而是Ruby on Rails)。Python也同样在演进。

现在有趣的是:整整一代程序员摒弃了命令行方式(这也是胶水语言运行的方式),希望在Web端可以做任何事情。从某方面来说,这样更好,比如在一个胶水程序中可以超链接到另一个胶水程序。从另一方面来说,则更糟糕,因为现在所有的Web程序都很慢,不能使用脚本,而且安装Electron的另一个副本需要500MB的RAM空间等等。这就引入了Web语言这个话题。

Web语言

图中,集中在JavaScript的“胶水”分支有很多的箭头指向,这并不奇怪。JavaScript最初只使用于前端。当Node.js出现后,这种情况完全改变了。现在,只需要学习一种语言来写前后端和命令行工具。JavaScript最初的设计是将其作为最终的胶水语言,试图融合HTML、CSS、面向对象编程、面向函数编程、动态语言、JITs以及其它一切能通过HTTP请求得到的东西。

但是这样不太好,因为后向兼容对于web的成功至关重要。要保证这一点,就无法修复一些严重错误。1995年,经过10天的设计,JavaScript发布了。对于10天的成果而言,它相当优秀,但同时它也存在一些问题,无法对其进行修复。

这就是图中唯一一个有双向箭头的地方:JavaScript和Python 3之间。我们把它叫做脚本语言的阴阳两面。

大部分出现过的胶水+Web语言正在消失,Python不在其列,至少目前还不会消失。我猜是因为Python本身是合理的。使用JavaScript编程时间足够长的话,过段段时间后就会变得不大正常。这时为了缓解压力,程序员有可能会切换到Python。

同时,如果长时间使用Python,最后准备编写Web应用程序时,前端代码和后端使用完全不同的语言是很烦人的。一个的语法是[‘a', ‘b', ‘c']. join(', '),而另一个则变成了’, '.join([‘a', ‘b', ‘c']),这让人完全记不清谁是谁了。

一种语言有JIT,可以让其一旦运行起来就会速度很快。而另一种则是启动快,运行慢。

一种有合理的命名空间系统,而另一种则没有。

我不清楚从长期看,Python 3是否能打败JavaScript。但至少目前看,它不会被击败。

同时,对于编程事实分支从不满意的Hejlsberg,看到了JavaScript的很多问题,引入了TypeScript。与此同时,微软突然停止了对Windows应用程序的大力推进,开始大面积推广Web和开源。这意味着Microsoft第一次将其开发者推向Web语言即JavaScript。在此基础上,他们有自己的TypeScript,我觉得这是一种很好的语言。这个分支存在有数十年,开始和其分支融合,可能不久后会消失。

TypeScript和JavaScript比,能胜出吗?这是个有趣的问题,我也不知道。我以前赌Hejlsberg能赢,不过我一般容易赌输。

Python 2和Python 3的对比

综上所述,我对Python 2和3有了结论。它们很相似,但不尽相同。我认为,这是因为他们在整个程序员语言迁移图中所处的位置不同。Python 2开发者来自C和perl开发人员,希望编写胶水代码。Web服务器是后续添加的一个应用场景。我的意思是,Python 2出现后,web程序变得流行起来,这并不出人意料。很多Python 2的开发者转到Go的开发,因为他们想写的某些“系统胶水”代码使用Go正合适。

Python 3的开发者是从不同的语言切换而来的。事实证明,Python 3问世后,Python的使用得到很大的发展,不过新加入的人群和以前的人群有所不同。由于带有模块SciPy和Tensorflow,从科学类和数值类处理转过来的新程序员占了其中很大的比例。老实说,在高吞吐量的数值处理中,Python是一个相当怪异的选择。但不论如何,这些库的存在是我们选择它的一个原因。我猜Python的另一个优势则是易于和C模块集成。当然,Python 3本身就是网络编程。

想要理解Python 2和3的区别,只需看看其不同的字符串类型。Python 2中,字符串是一组字节,因为操作系统、Unix管道处理、网络socket的处理均以字节为单位。对于系统程序而言,Python 2是胶水语言,其处理以字节为单位。

在Python 3中,字符串是一组unicode码。因为人们不擅长unicode码的转换,而和网络交互时,都是以unicode为基础。做科学数值计算的人不关心字符串,做网络编程的人更关心unicode,所以Python 3使用unicode。如果要用Python 3来编写系统程序,就会一直疲于unicode的转换,即使最简单的文件名也需要进行转换。这也正是有其因,必有其果。

相关文档

Misunderstanding Exceptions (2007)

You can't make C++ not ugly, but you can't not try (2010)