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

阅读数:3 2019 年 5 月 8 日

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

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

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

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

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

image

程序员迁移模式

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

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

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

  1. “低级”编程,包括 asm 和 C。
  2. “商业型”或“学习型”编程,从 BASIC 开始。
  3. 计算类 / 科技类编程,如 Fortran,MATLAB 和 R。
  4. 脚本 / 胶水编程,如 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)

原文链接
https://apenwarr.ca/log/20190318