快手、孩子王、华为等专家分享大模型在电商运营、母婴消费、翻译等行业场景的实际应用 了解详情
写点什么

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

  • 2019-05-08
  • 本文字数:6454 字

    阅读完需:约 21 分钟

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

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


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


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


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


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



程序员迁移模式


我想强调下最普遍的“终极节点”。在这些节点上,人们在他们所处的维度找不到更好的可替代编程语言。这些终极节点包括: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


2019-05-08 15:03120446

评论 1 条评论

发布
用户头像
Rust其实蛮受函数编程影响的,特别是trait的概念基本直接照搬了Haskell,所以应该多一个从Haskell到Rust的箭头。ES6的destructering assign其实也有点pattern matching的意思,Erlang程序员会觉得很熟悉。现在小众函数式语言程序员成为主流程序员也越来越容易了。
2020-07-13 10:46
回复
没有更多了
发现更多内容

Deno会在短期内取代Node吗?

葡萄城技术团队

node.js SpreadJS deno

Flink 从0到1学习—— Flink 不可以连续 Split(分流)?

zhisheng

大数据 flink 流计算

如何参与开源项目

郭旭东

GitHub 开源

一文搞懂RSA算法

somenzz

北大学子手写实现《统计学习方法》书中全部算法!

GitHubDaily

人工智能 GitHub 学习 程序员

Neo4j执行计划

脚动两轮男之漂流小王子

游戏夜读 | 数据整理的难题?

game1night

k8s上运行我们的springboot服务之——上传服务到docker私服

柠檬

Docker springboot

奈学教育分享:Hadoop分布式系统HDFS工作原理

奈学教育

hadoop hdfs 分布式

Flink 从0到1学习—— 分享四本 Flink 国外的书和二十多篇 Paper 论文

zhisheng

大数据 flink 流计算

Jenkins 插件开发之旅:两天内从 idea 到发布(下篇)

donghui

DevOps jenkins jenkins-plugin

露营之美,在乎山水之间也

李冬梅

JVM源码分析之堆内存的初始化

猿灯塔

《从0到1学习Flink》—— Flink parallelism 和 Slot 介绍

zhisheng

大数据 flink 流计算

2020年4月云主机性能评测报告

博睿数据

云计算 百度云 ucloud 性能测试 公有云

Jenkins 插件开发之旅:两天内从 idea 到发布(上篇)

donghui

DevOps jenkins jenkins-plugin

k8s上运行我们的springboot服务之——k8s 1.16.0安装

柠檬

k8s

如果你想做汽车开发,请先看看这篇。

水滴

自动驾驶 软件开发 开发

重学 Java 设计模式:实战工厂方法模式

小傅哥

设计模式 小傅哥 重构 架构设计 工厂模式

1分钱秒杀!疫情季,如何为孩子的升学保驾护航?

极客编

DDD 实践手册(番外篇: 事件风暴-实践)

Joshua

领域驱动设计 DDD 事件风暴 事件驱动 Event Storming

k8s上运行我们的springboot服务之——在linux安装docker并搭建docker私服

柠檬

Docker k8s

H2 的全文检索功能

Page

全文检索 lucene H2 内存数据库

《从0到1学习Flink》—— 你上传的 jar 包藏到哪里去了?

zhisheng

大数据 flink 流计算

Flink 从0到1学习 —— 如何使用 Side Output 来分流?

zhisheng

大数据 flink 流计算

招联金融助力经济复苏 致力成为“智慧生活的消费金融专家”

极客编

《从0到1学习Flink》—— Flink JobManager 高可用性配置

zhisheng

大数据 flink 流计算

你不知道的JSON.stringify(上)

前端黑板报

Java json

那个业务大拿死在了这个地方

小眼睛聊技术

Java 学习 高效工作 程序员 个人成长

职场提问的“唐太宗”原则

大伟

聊一聊采访外籍人员时需要注意的几点事项

李冬梅

态度 体验 感悟

一张主流编程语言的变迁图,讲清程序员迁移模式_编程语言_apenwarr_InfoQ精选文章