"The best way to communicate from one human being to another is through story."
— Donald Knuth
程序设计语言不仅是指令的集合,更是人类认知模型与机器物理执行之间的本体论中介。计算机语言哲学的一个矛盾在于“语义鸿沟”(Semantic Gap)——即人类解决问题的意图与计算机执行指令的底层逻辑之间的巨大差异。这一鸿沟不仅影响了编程的生产力,也直接关系到系统的可靠性与效率。因而我们可以看到有多种计算机语言“被设计”出来,它们的“语法”不一样,语言的使用规则代表了不同的设计哲学和效率取舍。
2025年,AI辅助编程已经迈过某个可用性的门槛,逐渐被更多人接纳,甚至用于软件生产。vibe coding 是否会成为一种新的语言?同样地,这种语言的背后意味着怎样的设计哲学、效率取舍,这股能量在生态层面和组织层面会释放出怎样的结果?这些都是未知的命题,因而站在今天这个节点,本篇文章对计算机编程语言历史进行了一段回顾和总结,看看有哪些元素对今天发生的一切有所启发。
本次分析主要源于一手访谈资料,特别是Lex Fridman播客系列。这些访谈提供了丰富、非结构化的讨论,设计师们在其中反思了他们的工作、设计哲学、遗憾以及对未来的展望。相比于学术论文或技术文档,这些访谈以一种更坦诚和哲学的方式捕捉了设计师们的思想,不仅揭示了他们做了什么,还揭示了他们为什么这么做。涉及的剧集包括:

编程语言的历史通常被呈现为一系列技术创新的接续:高级抽象的引入、面向对象范式的出现、动态类型的兴起,以及现代对内存安全的追求。这种叙事虽非错误,却掩盖了塑造该领域的更深层次的哲学潮流。它将编程语言视为在真空中设计出来的产物,仿佛它们从创造者的头脑中完全成型地诞生,而事实上,它们是特定历史时刻的产物,回应着具体的问题,并反映了其架构师的价值观和信念。
要更深入地理解编程语言,我们不仅要问“这门语言有什么特性?”,更要问“这门语言旨在解决什么问题?”,以及更根本地,“这门语言揭示了其创造者关于‘编程应该是什么’的何种哲学?”。例如,James Gosling明确将Java定位为对C/C++内存管理问题的回应,而Guido van Rossum则将Python定位为对Java等企业级语言冗长和复杂性的反应。Chris Lattner在设计Swift时,明确寻求将C++的性能、Java的安全性与Python的开发者工效学结合起来。
主导范式: 通过机器码和汇编语言直接操作硬件。第一代语言是直接作用于硬件的机器语言,由二进制数字组成,其语法完全取决于特定的机器架构 。随后出现的汇编语言(Second-Generation Languages)虽然引入了助记符和符号地址,但其逻辑依然高度依赖于计算机的物理结构 。
核心问题: 程序员必须管理计算的每一个细节,从CPU寄存器分配到内存管理。认知负担巨大,入门门槛极高。
缺陷:
架构师视角: Donald Knuth在反思他早期的编程经历时,描述了所需的脑力体操:
★“你看到宏观的东西,也看到微观的东西,并且你不自觉地在两者之间切换。你知道为了解决某个大问题,你需要做的是给某个寄存器加一,然后它会带你到另一步。我不会深入到电子层面,但我知道那些毫秒是什么,650上的磁鼓是什么样的。” [7]
这种在抽象层次之间跳跃的能力并非语言的特性,而是使用它的要求。语言没有提供抽象支持;程序员必须在头脑中维持所有抽象。
主导范式: 提供硬件抽象的高级语言(Fortran, ALGOL, COBOL, LISP)。
Brian Kernighan强调了这个时代的重要性:
★“Fortran是第一门允许程序员编写独立于特定机器架构代码的语言。这是一个根本性的突破。” [5]
核心创新: 突破在于可移植性。1957年创造的Fortran允许程序员编写可以在不同机器上运行的代码。这是一个革命性的变。FORTRAN 的出现标志着从“面向机器”到“面向问题”的转变。它允许程序员使用代数表达式解决科学计算问题,极大地缩小了数学逻辑与二进制指令之间的距离 。1958 年诞生的 ALGOL(Algorithmic Language)引入了块结构和嵌套作用域,为后续几乎所有的指令式语言奠定了结构化编程的哲学基础 。1958 年也是 LISP(List Processing)诞生的年份,John McCarthy 基于阿隆佐·丘奇(Alonzo Church)的 Lambda 演算提出了这种声明式编程范式,将计算视为数学函数的求值过程,而非单纯的状态变更。
但是这也引入的新问题:
GOTO 跳转,这导致了代码逻辑的混乱,即后来被称为“意大利面条式代码”的缺陷主导范式: C和UNIX,它们结合了低级控制与高级抽象。
核心创新:Kernighan 定义了著名的 Unix 哲学:编写只做一件事并做好它的程序;编写能够协同工作的程序;编写处理文本流的程序,因为这是通用的接口 。在 Kernighan 看来,编程的核心矛盾在于控制复杂性。 C在汇编语言的低级控制和高级语言的抽象之间取得了平衡。结合UNIX的简单、可组合工具的哲学,它创造了一个强大而高效的环境。
关键设计决策:
引入的新问题:
James Gosling指出了核心问题:
★“那是一个触发点。并发是一个大问题。你知道,因为当你与人互动时,你最不想看到的就是事情在等待。关于软件开发过程的问题。你知道,当故障发生时,你能从中恢复吗?你能做些什么来让创建和消除复杂数据结构变得更容易?你能做些什么来修复,你知道,最常见的C语言问题之一,也就是内存泄漏?以及它的邪恶双胞胎,被释放但仍在使用中的内存。” [2]
这个时期,Donald Ervin Knuth也提出了独特的编程哲学。Knuth 的哲学核心在于将编程视为一种“艺术”(Art),强调创造力与精密性的结合,而非单纯的技术技能 。他在1962年接受 Addison-Wesley 的委托,开始撰写《计算机程序设计艺术》(TAOCP。Knuth 对语言演化的贡献体现在他对“文学化编程”(Literate Programming)的倡导上。
他认为,传统的编程方法论倾向于告诉计算机做什么,而文学化编程则主张专注于向人类解释我们想要计算机做什么 。在早期的编程模式中,程序逻辑往往受限于编译器对文件结构的硬性要求,导致文档与代码分离,维护极难同步。
Knuth 开发了 WEB 系统,将 Pascal 源代码与 TeX 文档结合在一起 。 通过“编织”(Weaving)产生可读文档,通过“纠缠”(Tangling)产生机器执行代码,确保文档与实现的一致性 。 【note:或许这也是vibe coding的理念起源】
主导范式: 面向对象的语言(C++, Java),它们在试图管理复杂性的同时增加了抽象机制。这个阶段分裂成两种截然不同的哲学方法:
核心哲学: 在不牺牲性能的情况下为C添加强大的抽象机制。C++ 的设计目标是“管理复杂性而不牺牲速度” 。它将高级抽象(类、继承、模版)直接映射到硬件,确保没有“抽象层”位于程序员表达式与机器设施之间
关键创新:
设计原则: 零开销原则——抽象不应带来性能成本。Bjarne Stroustrup解释他的设计哲学:
★“我很快意识到我不能只是添加特性。如果你只是添加那些看起来漂亮、人们要求的或者你认为好的东西,一个接一个地加,你不会得到一个连贯的整体。你需要的是一套指导你决策的准则。其中一条规则就是零开销原则。” [1]
Stroustrup对原则驱动设计的承诺在他关于C++核心准则的工作中显而易见,该工作试图将好的C++编程的精髓提炼成一套连贯的原则。
但是这也引入的新问题:
核心哲学: 90 年代初,随着嵌入式设备(如机顶盒)和互联网的兴起,跨平台兼容性和安全性成为了核心诉求。James Gosling 在 Sun Microsystems 领导开发了 Java,其核心理念是“一次编写,到处运行”(WORA)Gosling 认为,C/C++ 的许多复杂性源于对内存的直接操作。Java 通过引入 Java 虚拟机(JVM)和强制性的垃圾回收(Garbage Collection, GC)彻底改变了编程模式
关键创新:
设计原则: 安全与保障至上;性能次之。
架构师视角: James Gosling解释了动机:
★“其中一件引人注目的事情是,他们正在做所有人们在20年前就已经在做的常规计算机事务。真正让我眼前一亮的是,他们似乎在重新发明计算机网络,并且正在犯计算机行业已经犯过的所有错误。此外,他们将自己与客户的关系视为神圣不可侵犯。他们从来、从来不愿意在安全性上做任何权衡。” [2]
JVM本身代表了一项根本性的创新:
★“Java虚拟机,你可以从不同角度看待它。大多数人没有真正意识到的一个观点是,你可以把它看作是抽象语法树的逆波兰表示法的编码(note:在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法、后序表示法)。另一种思考方式,也是它最终被解释的方式是,它就像一个抽象机器的指令集,其设计使得你可以将那个抽象机器翻译成物理机器。” [2]
但是同样也引入了新问题:
主导范式: 动态、解释型语言(Python, JavaScript, Ruby),它们优先考虑开发者生产力而非执行速度。
核心创新: 认识到对于许多任务,开发速度比执行速度更重要。如果一个程序运行需要10秒,但编写需要10分钟,那么优化执行速度是不理性的。
关键设计决策:
核心哲学: Python 的设计哲学强调代码的可读性 。Van Rossum 认为,代码被阅读的次数远多于编写的次数。著名的《Python 之禅》(PEP 20)提出了“优美胜于丑陋”、“显式胜于隐式”、“简单胜于复杂”等格言
关键创新:
架构师视角: Guido van Rossum解释了设计哲学:
★“作为一名程序员,拥有适用于不同任务的不同工具是很有用的。我仍然写C代码,我仍然写shell代码,但我大部分东西都用Python写。大多数时候,任务要求使用某种语言,因为任务不是从头开始写一个程序,而是在现有程序中修复一个bug或为一个大程序添加一个小功能。即使你不受上下文的约束,编写代码所需的时间和代码运行的时间之间也存在一种平衡。” [3]
Van Rossum的务实主义在他认识到不同任务需要不同工具这一点上显而易见。Python并非所有任务的最佳语言,但它是许多任务的最佳语言,特别是那些涉及快速原型设计和数据分析的任务。然而,Python的成功关键取决于社区:
★“我会说Python绝对是我做过的最好的事情。而且我不会只说是Python的创造,而是我‘抚养’Python的方式,就像一个孩子。我不仅构思了一个孩子,我还抚养了这个孩子。现在我正在让这个孩子自由地走向世界。” [3]
引入的新问题:
核心哲学: “更差即是更好”——一个简单、快速部署的解决方案可以战胜一个更优雅但复杂的方案。1995 年,网景(Netscape)的 Brendan Eich 在仅仅 10 天内开发出了 JavaScript 的原型 。这在语言设计史上是一个奇迹。JavaScript 当时面临着营销层面的严苛限制:它必须看起来像 Java,但不能像 Java 那样复杂;它被定位为 Java 的“小兄弟”,面向非专业程序员 。Eich 巧妙地融合了 C 的语法、Scheme 的一等函数(First-class functions)以及 Self 语言的“原型继承”(Prototypal Inheritance)
:** Brendan Eich解释了设计过程:
★“我有点像是在以一种狂热的方式描述‘更差即是更好’,因为它符合Netscape的模式。那不像你在做改变世界的先进科学研究。你更像是推翻了Mark做的上一代浏览器,那个有图像的浏览器,然后你正在创造Netscape,这个有图像、插件、框架、HTML表格的新东西。” [4]
关键创新:
关键的洞见在于,JavaScript的成功并非由于技术完美,而是由于分发:
★“分发。分发比任何事情都重要。这就是为什么即使现在我们看到在浏览器大战中Edge表现更好,因为它被强加给Windows用户。一旦你拥有了那个操作系统据点,你就可以强制推行Edge。苹果也对Safari做了同样的事。这并非微软独有。” [4]
引入的新问题:
主导范式: 通过专门的库(NumPy, SciPy)增强的Python,用于科学计算。
核心创新: 认识到一门语言的力量不来自其内置特性,而来自其库的生态系统。Python虽然慢,但通过NumPy和SciPy成为科学计算的主导语言。Travis Oliphant解释了设计哲学:
★“主要原则是为科学家和工程师提供可访问性,为他们提供工具,这样他们就不必过多地思考编程。这意味着给他们真正好的构建块,他们想要调用的函数,名字拼写长度恰到好处——信息丰富但简短。” [8]
关键的洞见在于可访问性比技术纯粹性更重要。科学家和工程师拥有领域专业知识,但可能不是专家程序员。工具应该利用他们的领域专业知识,而不是要求他们成为专家程序员。
关键设计决策:
引入的新问题:
主导范式: 为现代多核系统设计的语言(Go, Rust, Swift),它们试图结合性能、安全性和开发者工效学。
核心创新: 认识到现代计算从根本上是并发的。多核处理器无处不在,语言必须为并发提供一等支持。
核心哲学: 简洁与务实。Go拒绝了C++和Java的许多特性,转而采用一种更简单、内置并发支持的语言。
关键创新:
引入的新问题:
核心哲学: 通过编译时验证实现内存安全,而不牺牲性能。
关键创新:
引入的新问题:
核心哲学: 语言设计即用户界面设计。渐进式复杂性披露允许初学者简单入门,同时为专家提供强大功能。Chris Lattner解释了哲学:
★“我最喜欢一门编程语言的地方,是它能赋能高质量的库。那些富有表现力的库,然后感觉就像是语言本身自然集成的一部分。Swift中的一个例子是int和float,还有array和string之类的。这些都是库的一部分。Int不是硬编码在Swift中的。” [6]
关键创新:
Donald Knuth的哲学在我们这些架构师中是独一无二的,因为他主要不是语言设计师,而是算法理论家和将编程作为一种艺术形式的实践者。他的核心原则是编程既是科学也是艺术:
★“当人们谈论艺术时,它真正的,我的意思是,这个词的意思是某种非自然的东西。所以当你有人工智能时,‘art’(艺术)和‘artificial’(人工的)来自同一个词根,表示这是由人类创造的东西。然后它又有了更深一层的含义,通常是美术,这其中加入了美的元素,并且说,你知道,我们有艺术性地完成的事情。这意味着不仅是由人类完成的,而且是以一种优雅、带来愉悦并具有……我想是托尔斯泰对陀思妥耶夫斯基。但无论如何,是那部分说它做得很好,而不仅仅是与自然不同。” [7]
这个将艺术定义为人类创造的、优雅的、带来愉悦的东西的定义,是Knuth哲学的核心。他相信,评判编程不应仅仅看它是否能工作,还应看它是否优美,是否给程序员带来愉悦。
Knuth理解复杂系统的方法是通过文学编程,他将其描述为:
★“文学编程的思想实际上是试图通过至少从两个角度——形式化的和非形式化的——来看待某件事物,从而更好地理解它。如果我们试图理解一个复杂的东西,如果我们能从不同方式看待它。所以这实际上是技术写作的关键,一个好的技术作者,尽量不显得明显,但会把每件事说两遍,形式化地和非形式化地,或者可能三遍,但你试图给读者一种将概念放入他或她自己大脑的方式。” [7]
这一原则——理解需要多重视角——反映了一种深刻的认识论立场。Knuth相信,仅靠形式化数学不足以理解;我们还需要非形式化的解释、例子和直觉。
Knuth还强调计算思维作为理解世界的一种方式的重要性:
★“主要有两件事。一是跳跃抽象层次的能力。你看到宏观的东西,也看到微观的东西,并且你不自觉地在两者之间切换。你知道为了解决某个大问题,你需要做的是给某个寄存器加一,然后它会带你到另一步。我不会深入到电子层面,但我知道那些毫秒是什么,650上的磁鼓是什么样的。我知道我将如何分解一个数或找到一个方程的根或类似的事情,因为我知道它在做什么。” [7]
这种在抽象层次之间流畅移动的能力——从高级算法到低级机器操作——是Knuth所认定的“极客”思维的核心。它不仅是一种技术技能,更是一种理解世界的方式。
Brian Kernighan的哲学植根于UNIX哲学和C语言的设计。他的核心原则是通过泛化实现简洁:
★“UNIX效率高的部分原因在于它始于极其简陋的硬件,这强制要求机制的某种最小化和对泛化的寻求。UNIX中的文件系统就是这方面的一个好例子;文件系统接口在其基本形式上极其直接。找到做某事的正确模型意味着许多事情变得更简单,因此,也意味着更多的人可以用它们做有用、有趣的事情,而不必费力思考。” [5]
这一原则——找到正确的抽象可以使事情大大简化——是Kernighan哲学的核心。UNIX文件系统将一切都视为文件,这不仅是一种技术便利,更是一种更深层次原则的表达:统一抽象的力量。
Kernighan的编程方法从根本上是务实和实验性的:
★“这当然更像是非正式、增量的方法。我现在不写大程序。我写的许多程序都是实验,要么是我好奇的东西,要么通常是我希望在课堂上讨论的东西。我如今写的很多代码都倾向于用于探索性数据分析,我有一些数据集合,我想弄清楚里面到底发生了什么。” [5]
这段话揭示了Kernighan哲学的一个重要方面:编程从根本上是关于探索和发现的。工具应该支持这个探索过程,而不是阻碍它。这就是为什么Kernighan帮助设计的AWK在数据分析方面如此有效——它提供了强大的默认设置,使常见任务变得容易,同时又足够灵活以应对意外用途。
Kernighan也对当前软件开发的状况深感忧虑,特别是依赖项的爆炸式增长:
★“过去编程之所以有趣,原因之一是你真的是在自己构建一切。你需要处理的库数量非常少。也许是printf或标准库之类的。今天情况并非如此。如果你想用Python和JavaScript做点什么,你通常需要下载一大堆其他东西,而你根本不知道你得到的是什么。完全不知道。” [5]
这种担忧揭示了现代编程中的一种张力:虽然库和框架能够实现快速开发,但它们也引入了复杂性,并降低了程序员对其代码实际作用的理解。对Kernighan而言,这是一个严重的问题,因为它破坏了编程的根本目标,即理解和控制机器正在做什么。
Bjarne Stroustrup的哲学根植于一个基本信念:抽象不应带来性能开销。这个他称之为“零开销原则”的原则,不仅是一条技术准则,更是一种关于人类便利性与机器效率关系的哲学立场。
★“零开销原则基本上是说,如果你有一个抽象,它不应该比用更低层次的代码写出等效功能付出任何代价。所以,如果我有一个矩阵乘法,它的写法应该让你无法通过降到C语言的抽象层次,使用数组和指针等方式来运行得更快。” [1]
这一原则反映了Stroustrup的信念,即最优化的最有效方法不是为了速度牺牲抽象,而是设计本身就可优化的抽象。他认为,好的抽象通常比手动编写的低级代码带来更好的性能,因为它们能让编译器进行手动代码无法实现的优化。
Stroustrup哲学的核心是资源获取即初始化(RAII)的概念,体现在构造函数-析构函数对中。他认为这是C++最优雅的特性:
★“构造函数和析构函数。构造函数可以为对象的使用建立类型环境,而析构函数在最后清理任何混乱。这是C++的关键。这就是为什么我们不必使用垃圾回收。这就是我们如何能获得可预测的性能。这就是在许多情况下如何能获得最小的开销,并拥有真正干净的类型。” [1]
这反映了一种更深层次的哲学承诺:确定性资源管理优于非确定性垃圾回收。对Stroustrup而言,能够确切知道资源何时被释放,不仅是性能优化,更是构建可靠系统的基本要求。
Stroustrup的设计哲学还以对原则驱动设计的承诺为特征。他明确反对仅仅因为某些特性看起来有用就添加它们的方法:
★“我很快意识到我不能只是添加特性。如果你只是添加那些看起来漂亮、人们要求的或者你认为好的东西,一个接一个地加,你不会得到一个连贯的整体。你需要的是一套指导你决策的准则。” [1]
这种对原则而非特性的承诺,在他多年来开发的C++核心准则中显而易见。这些准则不是任意的规则,而是植根于关于好的C++代码应该是什么样的连贯哲学。
Stroustrup的世界观从根本上被他在重要系统上的工作所塑造——望远镜、火星探测器和关键基础设施。这个背景解释了他对性能和可靠性的坚定关注。他明确指出了他的语言适用的领域:
★“C++是为那些希望很好地利用硬件,然后通过抽象来管理这样做所带来的复杂性的人准备的……我不想让每个人都来编写我的飞机控制程序或汽车控制程序。我希望那是由工程师来完成的。我希望那是由受过专门教育和培训来构建事物的人来完成的,它不适合每个人。同样,像C++这样的语言也不适合每个人。它被创造出来是为了成为专业人士的锋利而有效的工具,基本上,并且绝对是为那些追求某种精确性的人准备的。” [1]
如果说Stroustrup的哲学植根于性能和抽象,那么James Gosling的哲学则植根于安全与保障。他创造Java的动机主要不是技术性的,而是道德性的:他观察到C/C++的内存管理模型造成的持续问题,并希望创造一种让一整类错误不可能发生的语言。
Gosling通往Java的旅程始于1990年代初对消费电子公司的公路旅行,在那里他观察到一些使他思想豁然开朗的事情:
★“其中一件引人注目的事情是,他们正在做所有人们在20年前就已经在做的常规计算机事务。真正让我眼前一亮的是,他们似乎在重新发明计算机网络,并且正在犯计算机行业已经犯过的所有错误。此外,他们将自己与客户的关系视为神圣不可侵犯。他们从来、从来不愿意在安全性上做任何权衡。” [2]
这一观察——安全性绝不应妥协——成为Java的指导原则。当被问及Java的创造时,Gosling指出了核心问题:
★“那是一个触发点。并发是一个大问题。你知道,因为当你与人互动时,你最不想看到的就是事情在等待。关于软件开发过程的问题。你知道,当故障发生时,你能从中恢复吗?你能做些什么来让创建和消除复杂数据结构变得更容易?你能做些什么来修复,你知道,最常见的C语言问题之一,也就是内存泄漏?以及它的邪恶双胞胎,被释放但仍在使用中的内存。” [22]
Java虚拟机(JVM)本身就是这种哲学的体现。JVM不仅仅是一种技术便利,它代表了对平台独立性和安全性的根本承诺。Gosling解释说:
★“Java虚拟机,你可以从不同角度看待它。大多数人没有真正意识到的一个观点是,你可以把它看作是抽象语法树的逆波兰表示法的编码。另一种思考方式,也是它最终被解释的方式是,它就像一个抽象机器的指令集,其设计使得你可以将那个抽象机器翻译成物理机器。” [2]
Gosling哲学中非凡之处在于它超越了技术关切,延伸到了伦理层面。当被问及他的遗产时,他以明确的道德术语来界定:
★“我开始思考我生活中的伦理选择。因为我是一个科幻迷,我开始从我做的几乎每一个技术决策的角度思考,你是在建造《银翼杀手》还是《星际迷航》?你更愿意生活在哪个未来?” [2]
这个伦理维度并非Gosling思想的边缘,而是其核心。他认为编程语言的选择和系统的设计具有深远的道德影响。Java的安全性和可靠性不仅仅是技术特性,而是对构建服务于人类繁荣而非助长剥削或伤害的系统的承诺的表达。
在Stroustrup优先考虑性能、Gosling优先考虑安全的地方,Guido van Rossum则优先考虑可读性和开发者生产力。他的设计哲学被《Python之禅》中的名言所概括:“可读性很重要。” Van Rossum的语言设计方法从根本上是务实的。他明确拒绝存在一种“正确”编程方式的观点:
★“作为一名程序员,拥有适用于不同任务的不同工具是很有用的。我仍然写C代码,我仍然写shell代码,但我大部分东西都用Python写。大多数时候,任务要求使用某种语言,因为任务不是从头开始写一个程序,而是在现有程序中修复一个bug或为一个大程序添加一个小功能。即使你不受上下文的约束,编写代码所需的时间和代码运行的时间之间也存在一种平衡。” [3]
这段话揭示了van Rossum的核心洞见:语言的选择应由具体任务和上下文决定。对于许多任务,编写和调试代码所花费的时间远远超过运行它的时间。在这种情况下,优先考虑开发速度而非执行速度的语言是理性的选择。
Python的设计反映了这种务实的哲学。Van Rossum有意牺牲执行速度来换取开发速度,接受了动态类型和解释执行的局限性,以换取一种易于学习和使用的语言。这个决定并非出于对成本的无知,而是一种有意识的权衡:
★“当你探索解决方案时,你通常花在编写代码上的时间比运行它的时间要多,而像Python这样的语言使得这种迭代快得多。在你的程序编译和运行之前,你需要弄对的细节更少,而且有各种各样的库为你做各种事情。你可以快速地将现有组件组合在一起,让你的原型应用程序运行起来。” [3]
Van Rossum哲学中特别有趣的一点是他认识到语言设计从根本上是关于社区的。他不仅将自己视为Python的创造者,还视自己为其管理者:
★“我会说Python绝对是我做过的最好的事情。而且我不会只说是Python的创造,而是我‘抚养’Python的方式,就像一个孩子。我不仅构思了一个孩子,我还抚养了这个孩子。现在我正在让这个孩子自由地走向世界。而且我已经把这个孩子培养得能够照顾好自己。” [3]
这个“抚养”语言的比喻揭示了van Rossum哲学的一个基本方面:语言的成功不取决于其初始设计,而取决于它如何在社区中演进。Python社区强调清晰沟通和共识建立,这并非Python成功的偶然因素,而是其核心。
Brendan Eich的哲学与Stroustrup和Gosling那种谨慎、原则驱动的方法形成鲜明对比。Eich在十天内,在极大的时间压力下创造了JavaScript,他的设计哲学反映了这一背景。他明确拥抱他所谓的“更差即是更好”(Worse is Better)哲学:
★“我有点像是在以一种狂热的方式描述‘更差即是更好’,因为它符合Netscape的模式。那不像你在做改变世界的先进科学研究。你更像是推翻了Mark做的上一代浏览器,那个有图像的浏览器,然后你正在创造Netscape,这个有图像、插件、框架、HTML表格的新东西。Mark和那里的工程团队认为我们需要让这个浏览器变得可编程,而不仅仅是一个文档查看器,不仅仅是一个视频播放器。” [4]
由Richard Gabriel阐述的“更差即是更好”哲学认为,一个简单、快速部署的解决方案,即使不完美,也能战胜一个更优雅但复杂的方案。对Eich而言,这不仅是一个务实的选择,更是对技术采纳基本真理的认识:
★“你可以说在几乎任何网络系统中,就像生物进化一样,你看到成功的等位基因席卷种群,而它们并不总是,你知道,没有缺陷的,比如杂合子优势,对吧?你可以得到双亲的基因。这不是一个美丽的过程,除非是在大规模上。因为它移动迅速并且能够适应,它就能赢。” [4]
这种进化论的视角对于理解Eich的哲学至关重要。他认为技术的成功不是由其技术完美性决定的,而是由其传播和适应的能力决定的。JavaScript的成功不是由其优雅的设计保证的,而是由其在每个网络浏览器中的存在保证的。
当被问及他最大的遗憾时,Eich指向了那个草率的比较运算符——一个在最初十天冲刺后引入的设计缺陷。但他也阐明了一个深刻的教训:
★“我从中得到的教训,我也在推特上发过,就是当人们来找你说,‘你能把它弄得更草率一点吗,加这个可爱的特性’,答案应该是‘不’。我本该知道的,因为我的英雄之一Niklaus Wirth说过,‘设计的本质在于舍弃’。” [4]
这段话揭示了Eich思想中的一种张力:虽然他拥抱“更差即是更好”的哲学,但他也认识到简洁和克制的价值。JavaScript的问题不在于它简单,而在于它以错误的方式简单——它包含了一些本应被舍弃的特性。
Eich最重要的洞见关乎分发的首要性:
★“分发。分发比任何事情都重要。这就是为什么即使现在我们看到在浏览器大战中Edge表现更好,因为它被强加给Windows用户。一旦你拥有了那个操作系统据点,你就可以强制推行Edge。苹果也对Safari做了同样的事。这并非微软独有。” [4]
这一论断挑战了技术优点决定成功的普遍假设。对Eich而言,分发——触及用户的能力——比技术质量更重要。这对于理解为什么JavaScript尽管有许多缺陷,却成为世界上使用最广泛的编程语言具有深远的影响。
Chris Lattner他将编程语言设计视为一种用户界面设计,程序员就是用户:
★“我们有这些叫做计算机的野兽,它们在特定类型的事情上非常擅长,我们认为让它们为我们做这些事很有用。现在你面临一个问题,如何最好地表达这一点,因为你仍然有一个人脑,脑子里有一个想法,你想实现某个目标。你可以直接与机器对话,说汇编语言,然后你可以直接表达计算机所理解的东西。然后你可以有越来越高层次的抽象,直到机器学习,当你设计一个神经网络来为你工作时。问题是,在这条路上你想到哪里停下来,这样做有什么好处?” [6]
这种框架——将语言设计视为为任务选择正确抽象层次的问题——是Lattner思想的特征。他并不致力于任何特定的抽象层次,而是致力于为具体问题选择正确的层次。
Lattner设计哲学中的一个关键原则是渐进式复杂性披露:
★“我们非常关心能力,非常关心效率。好的设计有很多因素,你必须想办法在其中找到一条路。” [6]
这一原则借鉴自用户界面设计,它表明一门好的语言应该对初学者易于学习,同时为专家提供高级特性。Swift的设计体现了这一原则:初学者可以编写简单的代码而无需理解泛型或面向协议编程等高级特性,而专家可以利用这些特性编写复杂的代码。
Lattner也深切关注赋能高质量的库:
★“我最喜欢一门编程语言的地方,是它能赋能高质量的库。那些富有表现力的库,然后感觉就像是语言本身自然集成的一部分。Swift中的一个例子是int和float,还有array和string之类的。这些都是库的一部分。Int不是硬编码在Swift中的。因为int只是标准库中定义的一个库类型,与字符串、数组和标准库附带的所有其他东西一样,定义int所需的任何语言特性,你也可以在自己的类型中使用。” [66]
Travis Oliphant的哲学植根于对可访问性的承诺。他创造NumPy并非作为理论练习,而是为了解决一个实际问题:让科学计算对那些主要不是程序员的科学家和工程师来说变得可访问。
★“主要原则是为科学家和工程师提供可访问性,为他们提供工具,这样他们就不必过多地思考编程。这意味着给他们真正好的构建块,他们想要调用的函数,名字拼写长度恰到好处——信息丰富但简短。我们还想要绘图、一个交互式环境和好的文档,尽管考虑到我们有限的预算和志愿劳动,这些花了大约10年才发展起来。” [8]
这一原则——编程工具应为领域专家设计,而不仅仅是程序员——反映了对技术实际使用方式的深刻理解。科学家和工程师拥有领域专业知识,但可能不是专家程序员。工具应该利用他们的领域专业知识,而不是要求他们成为专家程序员。
Oliphant也深切关注开源的经济学:
★“我研究了资本主义和马克思主义,并接触到继承亚当·斯密关于涌现社会思想的著作。我意识到经济学的挑战也是政治性的,不同党派都希望经济学家支持他们。我读了冯·米塞斯的《经济计算问题》,该书认为没有私有财产来创造价格体系,社会主义联邦是行不通的。价格是重要的信号机制,而货币只是一种易货工具。” [8]
这种对经济理论的涉猎在语言设计师中并不常见,但它反映了Oliphant的认识:开源的可持续性需要行之有效的经济模型。NumPy和SciPy的成功不仅取决于其技术质量,还取决于支持其发展的经济激励措施。
Oliphant的哲学还强调社区与协作的重要性:
★“倾听有问题的人,并且倾听很多人。做一个实验,不要害怕失败。你做的第一件事很可能很糟糕,那没关系。迭代是创新的关键。它不需要高度集中。好事不会仅仅通过滚动屏幕发生。你需要真诚的深度工作时间。” [8]
这段话抓住了Oliphant方法的精髓:成功的工具是通过与用户的迭代互动构建的,而不是通过孤立的天才。社区并非设计过程的边缘,而是其核心。
一旦一门语言被广泛采用,其早期的设计决策就几乎不可能改变。这造成了永久性的技术债务,限制了未来的演进。
JavaScript的草率比较运算符: Brendan Eich明确对自己草率的比较运算符感到后悔,这是在最初十天冲刺后引入的:
★“我从中得到的教训,我也在推特上发过,就是当人们来找你说,‘你能把它弄得更草率一点吗,加这个可爱的特性’,答案应该是‘不’。我本该知道的,因为我的英雄之一Niklaus Wirth说过,‘设计的本质在于舍弃’。” [4]
然而,由于网络对向后兼容性的要求,这个设计缺陷无法修复。它至今仍存在于JavaScript中,成为三十年前一个仓促决定的永久提醒。
Python 2到Python 3: 从Python 2到Python 3的过渡需要打破向后兼容性来修复基本的设计问题。这是一个痛苦的过程,耗时十多年,并使社区分裂。Guido van Rossum反思道:
★“我们一直非常认真地对待向后兼容性,早期版本中的许多Python‘瑕疵’已经得到解决,因为它们可以在保持向后兼容性的同时解决。我们被一些被广泛认为是问题的东西困住了,不是障碍,而是有些人会绊倒的东西,我们想不出一种向后兼容的方式来解决这些问题。” [3]。
仅靠技术优点并不能决定成功。生态系统——围绕一门语言的库、工具和社区——通常比语言本身更重要。
JavaScript的成功: 尽管有许多技术缺陷,JavaScript成为世界上使用最广泛的编程语言。Brendan Eich将其归因于分发而非技术卓越:
★“分发。分发比任何事情都重要。” [4]
JavaScript在每个网络浏览器中的存在保证了它的采用,无论其技术优点如何。
Python的成功: Python在科学计算中的主导地位并非由于Python的执行速度——Python是出了名的慢。相反,它归功于NumPy、SciPy和更广泛的科学库生态系统。Travis Oliphant解释说:
★“你可以做的是使用像NumPy这样的包,还有许多其他非常强大的包,它们可以利用所有可用的CPU,因为你告诉包‘这是数据,这是要应用在其上的抽象操作。去吧。’然后我们就回到了C++的世界。但那些包本身通常是用C++实现的。” [3]
这揭示了一个关键的洞见:Python的力量不来自语言本身,而来自其通过像NumPy这样的库与高性能C/C++代码接口的能力。
Java的成功: Java在企业计算中的主导地位并非由于其技术上优于C++或Python,而是由于围绕它发展的框架(Spring, Hibernate)、工具(Maven, Gradle)和社区的生态系统。
每种语言设计都涉及权衡。没有普遍的“最佳”语言,只有为特定上下文和用例优化的语言。
这些权衡并非任意的,而是反映了语言设计师的价值观和优先事项。理解一门语言需要理解这些权衡。
除了具体的设计决策和权衡之外,这些访谈还揭示了关于编程语言是什么以及它们应该是什么的更深层次的哲学洞见。
一门编程语言不仅是一种技术工具,更是其创造者关于“编程应该是什么”的价值观和信念的反映。
Stroustrup的价值观: 性能、抽象和精确。C++是为那些希望有效利用硬件同时通过抽象管理复杂性的专业人士设计的。具体可见Stroustrup的零开销原则
Gosling的价值观: 安全和保障。Java旨在消除整类错误。
Van Rossum的价值观: 可读性、生产力和社区。Python被设计为易于学习和使用,并非常强调社区和协作。 这一原则指导着Python的设计。它解释了为什么Python使用有意义的空白(这提高了可读性),尽管受到一些程序员的批评。
Eich的价值观: 务实主义和分发。JavaScript被设计为可以快速、广泛地部署,即使不完美。
这些价值观并非语言的附带品,而是其根本。理解一门语言需要理解其创造者的价值观。
编程语言不仅是指导机器的工具,也是思考的工具。不同的语言赋能不同的思维方式。
Knuth的洞见: “跳跃抽象层次”的能力是计算思维的核心。一门语言应该支持这种能力。
Lattner的洞见: 语言设计即用户界面设计。语言应该被设计来支持程序员实际思考问题的方式。
Kernighan的洞见: 正确的抽象可以使复杂问题变得简单。一门语言应该提供与问题领域相匹配的抽象。
有趣的是,许多最成功的设计决策是由约束而非深思熟虑的选择驱动的。
C的简洁性: C的简洁性部分是由1970年代有限的硬件驱动的。Kernighan解释说:
★“UNIX效率高的部分原因在于它始于极其简陋的硬件,这强制要求机制的某种最小化和对泛化的寻求。” [5]
JavaScript的设计: JavaScript是在十天内,在极大的时间压力下设计的。Eich解释说:
★“我有点像是在以一种狂热的方式描述‘更差即是更好’,因为它符合Netscape的模式。” [4]
这些约束,非但没有成为障碍,反而导致了优雅的解决方案。硬件的限制迫使C的设计师寻找通用的抽象。时间压力迫使Eich保持JavaScript的简洁。
编程语言演进并非一个简单的技术进步故事。它是一个关乎哲学、妥协,以及对表达人类逻辑的更好工具的不懈追求。八位架构师——Stroustrup, Gosling, van Rossum, Eich, Kernighan, Lattner, Knuth和Oliphant——每位都带来了关于“编程应该是什么”的独特愿景。这些愿景并非相互冲突,而是代表了价值观多维空间中的不同点。为一组价值观优化的语言必然会牺牲其他价值观。编程语言的历史就是探索这个空间、寻求更好的平衡和综合的历史。
从本分析中浮现出几个关键洞见:
设计是权衡的艺术。 没有完美的语言,只有为特定上下文和用例优化的语言。理解一门语言需要理解它优先考虑什么,牺牲什么。
生态系统优于技术纯粹性。 一门语言的成功更多地取决于其生态系统——其库、工具和社区——而非其技术特性。JavaScript尽管有缺陷却成功,以及Python尽管慢却在科学计算中占主导地位,都证明了这一原则。
原则指导设计。 成功的语言设计师是由清晰的原则(Stroustrup的零开销原则、van Rossum的可读性原则、Lattner的渐进式披露原则)指导的,而非临时的特性添加。
早期决策具有持久的影响。 一旦一门语言被广泛采用,其早期的设计决策就几乎不可能改变。这造成了对未来演进的永久性约束。
语言设计从根本上是关于价值观的。 一门编程语言反映了其创造者关于“编程应该是什么”的信念。理解一门语言需要理解这些价值观。
辩证模式仍在继续。 每一代语言都作为对前一代局限性的反应而出现,引入新的解决方案,同时创造出将被下一代语言解决的新问题。
编程语言的未来很可能由新兴的挑战所塑造:
为应对这些挑战而出现的语言将反映出性能与安全、抽象与控制、简洁与强大之间永恒张力的新综合。编程语言的历史最终是人类持续寻求更好的方式来思考和与机器交流的历史。正如这八位架构师向我们展示的那样,总有新的想象方式,而旅程仍在继续。
[1] Lex Fridman. (2019). Bjarne Stroustrup: C++ [播客]. Lex Fridman Podcast #48. https://podwise.ai/dashboard/episodes/2555851
[2] Lex Fridman. (2020). James Gosling: Java, JVM, Emacs, and the Early Days of Computing [播客]. Lex Fridman Podcast #126. https://podwise.ai/dashboard/episodes/2555624
[3] Lex Fridman. (2018). Guido van Rossum: Python [播客]. Lex Fridman Podcast #6. https://podwise.ai/dashboard/episodes/2555950
[4] Lex Fridman. (2021). Brendan Eich: JavaScript, Firefox, Mozilla, and Brave [播客]. Lex Fridman Podcast #160. https://podwise.ai/dashboard/episodes/2555580
[5] Lex Fridman. (2020). Brian Kernighan: UNIX, C, AWK, AMPL, and Go Programming [播客]. Lex Fridman Podcast #109. https://podwise.ai/dashboard/episodes/2555652
[6] Lex Fridman. (2020). Chris Lattner: The Future of Computing and Programming Languages [播客]. Lex Fridman Podcast #131. https://podwise.ai/dashboard/episodes/2555619
[7] Lex Fridman. (2019). Donald Knuth: Algorithms, Complexity, and The Art of Computer Programming [播客]. Lex Fridman Podcast #62. https://podwise.ai/dashboard/episodes/2555786
[8] Lex Fridman. (2021). Travis Oliphant: NumPy, SciPy, Anaconda, Python & Scientific Programming [播客]. Lex Fridman Podcast #224. https://podwise.ai/dashboard/episodes/2555508