/writing

避免成功

10 分钟

"Avoiding success at all cost" 是 Haskell 的非官方座右铭,出自 Simon Peyton Jones 之口。在他 2017 年的演讲 Escape from the Ivory Tower 里,他放出这张 slide 时带着一个会心的笑,台下也笑了,因为这句话的歧义本身就是重点。

它有两种读法:"Avoiding (success at all cost)",不要为了流行而不惜一切代价。或者 "(Avoiding success) at all cost",不惜一切代价,保持小众。

妙的是两种读法都是真的,而且互相成就。Haskell 保持了纯粹是因为它保持了小众,而它保持了小众是因为它保持了纯粹。这个正反馈,这种用流行度换完整性的意愿,正是让 Haskell 之所以成为 Haskell 的东西。

语言设计中的成功与代价

任何软件设计都存在取舍,编程语言设计也不例外。

所有使一门语言「成功」的设计决策(因某些原因在工业界或教育界被广泛使用),都伴随着各自的「代价」:不安全、表达力有限、或者性能不佳。

「代价」是否构成问题,取决于场景和目标。Python 和 JavaScript 都因动态类型而富有表达力且对初学者友好,但牺牲了类型安全和性能。Java 则恰恰相反,使用了更安全、更利于优化的类型系统,但表达力弱得多。类似的取舍也出现在内存管理上:「托管」语言(无论是 ARC 还是垃圾回收)对大多数程序员来说更容易、更安全,但也被认为比「更接近底层」的语言更慢。

这些代价都没有真正阻止一门语言长盛不衰。大多数语言找到一个甜蜜点,接受取舍,然后继续前行。

Haskell 拒绝了这笔交易。

以研究为导向意味着目标是追求某些「终极」的东西:终极简洁的中间表示、安全性与表达力可以共存的终极类型系统、终极的编译速度和运行时性能、终极简明优雅的具体语法……必须是某些非常困难、可能无尽且不可能完全实现的终极目标。

这使得 Haskell 中的每一个语言设计决策都异常艰难和缓慢,因为几乎没有什么可以被牺牲。这就是为什么 Haskell 坚持使用惰性求值来「守护」纯粹性,尽管 call-by-need 存在种种问题。这就是为什么项目启动后的头四年里都没有一个像样的 IO 机制,直到 Philip Wadler 发现了 Monad。这就是为什么 Type Class(最早在 Wadler 1989 年的论文中提出)花了好几年才得以实现和普及。

这并不意味着 Haskell 中完全没有妥协,只是在发展过程中被尽可能地最小化了。当一位听众问为什么 Haskell 和 OCaml 这两门在高层面上相当相似的语言都存活了下来时,SPJ 回答道:

There's just a different set of compromises.(只是一组不同的妥协罢了。)

语言设计过程中的成功与代价

编程语言设计中另一个常见但极具争议的话题是关于设计过程本身:你更倾向于独裁还是委员会(换句话说,多人的独裁)?你更倾向于私有还是标准化?你会用什么形式来编写标准:自然语言、伪代码还是形式化语义?你敢做多少、多频繁的破坏性变更?你会让开源社区参与进来吗?

我认为对所有这些问题都没有标准答案。大多数流行的编程语言走过的路径各不相同,而且仍在继续走着各自的路。

Python 的创造者 Guido van Rossum,被称为「仁慈的终身独裁者」(BDFL,Benevolent Dictator For Life)。在 Python 流行并采用开源和社区驱动的开发模式后,他仍然(直到 2018 年 7 月)在 Python 的开发中扮演着核心角色。这一因素直接促成了 Python 3 的落地。这是一个具有破坏性(不完全向后兼容且不易移植)但在语言设计和一致性方面很好的语言修订,尽管面临着来自许多社区的压力。很多语言(Ruby、Perl、Elm)也选择了类似的路线。

JavaScript,众所周知由 Brendan Eich 在 10 天内创建,相比之下,由于 Web 的开放本质和自身的快速普及,很快就演变成了一门由委员会(TC39)管理并标准化(ECMAScript)的语言。但 Brendan 作为创造者,甚至没有足够的权力推动委员会落地 ES4(那是一个具有破坏性但好得多的修订版)。最终的结果是 ES5(Harmony),一个向后兼容但野心小得多的版本,原因是不同方(Mozilla、Microsoft、Yahoo 等)之间诸多的政治斗争,历史因此未能被改写。即便是最近兴起的、每年发布的「现代」JavaScript(ES6 即 ES2015、2016、2017……)也主要由新一代的委员会参与方(加上 Google、Facebook、Airbnb 等)推动,并且仍然以非常开放和标准化的方式进行。

即便是两门相当相似的语言,它们的历史和发展过程也可以如此不同,更不用说更具私有性质的语言,如 Sun/Oracle 的 Java、Microsoft 的 C#、Apple 的 OC/Swift(尽管后者已开源),或者更具学术性和标准化的语言,如 SML 和 Scheme(它们都有用形式化语义编写的标准)。

因此,Haskell 选择了自己独特的过程来适配其独特的目标,这并不令人意外。尽管它根植于学术界,却选择了一种相当实用、不那么形式化的方式来定义语言:以编译器实现为准而非标准化(当然论文中有很多「形式化」的片段),从这个角度看更像 C++/OCaml。它有一个委员会,但不是非常开放和保守的那种,而是更加独裁的(相对于普通用户而言),在破坏性变更方面也极其激进。然而结果是,它培养了社区中一群非常能容忍变化的人。所有这些怪癖组合在一起运作得非常好,帮助 Haskell 避免了「太快地变得太成功」。

反讽

这个座右铭最美妙的地方在于:Haskell「避免了成功」,但它的 ideas 没有。它们只是穿着别人的衣服出现了。

Monad 变成了 Promise 和 async/await。Type Class 变成了 Rust 的 trait。代数数据类型(ADT)变成了 TypeScript 的 discriminated union。模式匹配正在被加入 Python、C# 和 Java。不可变性作为默认值通过 React 和 Elm 进入了主流。甚至 Maybe/Option 类型,Haskell 对 null 的回答,现在存在于 Swift、Rust、Kotlin 以及几乎每一门 2010 年之后设计的现代语言中。

Haskell 没有大规模普及(mass-adopt),它大规模授粉(mass-pollinate)了。象牙塔原来是一座灯塔。

在我自己的领域里也能看到这种影子。React 在 2013 年 JSConf 上第一次展示 JSX 时被群嘲。Hooks 被斥为黑魔法。Concurrent Mode 为了正确性打破了向后兼容。这些选择让 React 更难被接受,但它们是对的。

尾声

SPJ 把他的演讲命名为 "Escape from the Ivory Tower"。但我觉得真正的逃逸从来不是 Haskell 变成一门工业语言。而是 Haskell 的 ideas 逃逸了,进入了数以百万程序员使用的语言、框架和心智模型中,而这些程序员可能一辈子不会写一行 Haskell。

这或许才是避免成功的最成功的方式。

AI-translated from the original