关于语言设计的五个问题
原文:Five Questions about Language Design
作者:Paul Graham 发表:2001-05
译者:Claude(baoyu-translate)
2001 年 5 月
(这是我为 2001 年 5 月 10 日 MIT 一场关于编程语言设计的 panel 讨论所做的笔记。)
I. 好语言设计的原则
1. 编程语言是为人服务的。
编程语言是人和电脑说话的方式。电脑要是用任何无歧义的语言说话,都会同样高兴。我们之所以有高级语言,是因为人受不了机器语言。编程语言的意义,是防止我们脆弱可怜的人脑被海量细节淹没。
建筑师都知道——某些设计问题比另一些更“个人“。最干净、最抽象的设计问题之一是设计桥梁——你的工作大致就是用最少的材料跨越一段给定距离。光谱的另一端是设计椅子——椅子设计师得花时间琢磨人的屁股。
软件也是同样的光谱。设计在网络中路由数据的算法是个漂亮、抽象的问题,像设计桥梁。而设计编程语言像设计椅子:它的全部都是关于应付人的弱点。
我们大多数人都不愿意承认这一点。设计具有数学优雅性的系统,听起来比“迁就人的弱点“诱人得多。数学优雅当然有它的位置——某些种类的优雅会让程序更容易理解。但优雅本身不是目的。
我说“语言必须设计得迁就人的弱点“,不是说语言要为糟糕的程序员而设计。事实上我认为你应该为最好的程序员而设计——但即便最好的程序员也有局限。我不觉得有谁会喜欢用一门“所有变量都是 x 加整数下标“的语言编程。
2. 为你自己和你的朋友设计。
如果你看编程语言史,最好的那一批很多是为它们的作者自己用而设计的;最糟的那一批很多是为别人用而设计的。
当语言为别人设计时,那个“别人“总是某个具体的群体——没有语言设计者聪明的人。所以你得到一门对你居高临下的语言。Cobol 是最极端的例子,但许多语言都弥漫着这种气味。
这与语言有多抽象无关。C 相当底层,但它是为它的作者自己用而设计的——这正是黑客喜欢它的原因。
主张“为糟糕的程序员设计语言“的论据是:糟糕的程序员比好程序员多。也许吧。但那少数好程序员写出了不成比例的大部分软件。
我感兴趣的问题是——你怎么设计一门最顶尖的黑客会喜欢的语言?我恰好认为这等同于:你怎么设计一门好的编程语言?——但即便不等同,至少也是个有意思的问题。
3. 把尽可能多的控制权交给程序员。
许多语言(尤其是为别人设计的那些)有一种保姆的姿态——它们试图阻止你做’它们认为对你不好’的事。我喜欢相反的做法——把尽可能多的控制权交给程序员。
我第一次学 Lisp 时,最让我喜欢的是——它把我当作平等的伙伴。在我之前学过的别的语言里,“语言“是一回事,“我用语言写的程序“是另一回事——两者截然分开。但在 Lisp 里,我写的函数和宏,跟构成语言本身的那些一模一样。我想的话,我可以重写这门语言。它有一种和开源软件一样的吸引力。
4. 追求简洁。
简洁被低估,甚至被嘲笑。但如果你看进黑客的心里,你会发现他们真的爱它。你听到过多少次黑客深情地谈起:在比如 APL 里,他们怎样用区区几行代码做到惊人的事?我觉得真正聪明的人真心热爱的东西,都值得留意。
我觉得几乎任何能让程序变短的事都是好的——应该有大量库函数;任何可以隐式的东西都该是隐式的;语法应该简洁到极限;甚至东西的名字也该短。
而且不只程序该短——手册也该薄。手册的相当一部分被各种澄清、保留意见、警告、特殊情况占去了。如果你逼自己缩短手册,最好的情况下你会通过修复语言里那些需要这么多解释的地方来做到。
5. 承认黑客在做什么。
很多人希望黑客是一种数学——或者至少是某种自然科学。我觉得黑客更像建筑。建筑和物理有关——建筑师必须设计不会塌的房子——但建筑师真正的目标是造出伟大的建筑,而不是去发现关于静力学的新东西。
黑客喜欢做的事是写出伟大的程序。我觉得——至少在我们自己心里——我们必须记住:写出伟大的程序是一件令人钦佩的事,哪怕这份工作不容易换算成研究论文那种’惯例上的智识货币’。从智识上讲,设计一门程序员会爱的语言和设计一门糟糕的、但体现了某个能写论文的想法的语言——同样有价值。
II. 开放问题
1. 怎样组织庞大的库?
库正在成为编程语言越来越重要的组成部分。它们也正在变得越来越大——这件事可能很危险。如果找一个能做你想做的事的库函数比你自己写一个还要花时间,那这一堆代码就只在做一件事:把手册撑厚。(Symbolics(1980 年代美国 Lisp 机厂商)的手册就是个例证。)所以我觉得我们必须研究组织库的办法。理想情况是——把库设计成程序员能猜到哪个库调用会做正确的事。
2. 人们真的怕前缀语法吗?
这是个开放问题——我已经为它困惑了很多年,至今没答案。前缀语法对我来说完全自然——可能数学除外。但 Lisp 不流行的相当一部分原因,也许仅仅是因为它有一种陌生的语法。如果这是真的,要不要为此做点什么——又是另一个问题。
3. 服务器端软件需要什么?
我觉得未来 20 年里写出的最激动人心的新应用有相当一部分会是基于 Web 的应用——也就是坐在服务器上、通过 Web 浏览器和你说话的程序。要写这种程序,我们可能需要一些新东西。
我们需要的一件事是——为服务器端 app 的新发布方式提供支持。它们不像桌面软件那样一年只有一两次大发布,而是作为一连串小改动发布出来。一天可能多达五到十次发布。而且按惯例,每个人始终都用最新版。
你知道怎么把程序设计得可调试吧?基于服务器的软件同样必须被设计得可改。你必须能轻易地改它——或者至少能知道什么是小改动,什么是重大改动。
另一个对服务器端软件可能意外有用的东西是续延(continuations)。在基于 Web 的软件里,你可以用类似续延传递风格的东西,在 Web 会话天然无状态的世界里制造出’子程序’的效果。如果代价不太高,也许有真正的续延是值得的。
4. 还有哪些新抽象等待被发现?
我不太确定这希望有多合理——但我个人真心想做的一件事是:发现一种新抽象——重要程度堪比“一等公民函数“、“递归”、甚至“关键字参数“的那种。这也许是一个不可能的梦——这种东西并不常被发现。但我一直在找。
III. 服务器端软件给语言设计带来的不同
1. 你想用什么语言就用什么语言。
写应用程序,过去意味着写桌面软件。而在桌面软件里,有一种很大的偏向——把应用用和操作系统同一种语言来写。所以十年前,写软件几乎就是用 C 写软件。最后形成了一种传统:应用程序不能用奇怪的语言写。这种传统形成的时间够长,连像经理和风投这种非技术人士也学会了它。
基于服务器的软件把整套这个模型炸飞了。基于服务器的软件,你想用什么语言就用什么语言。几乎没人意识到这一点(经理和风投尤其没意识到)。少数黑客意识到了——这正是为什么我们能听到 Perl、Python 这种新的、独立的语言。我们不是因为有人用 Perl 和 Python 写 Windows 应用才听到它们的。
对我们这些对设计编程语言感兴趣的人来说,这意味着——我们的工作现在有了一群潜在的真实听众。
2. 速度来自 profiler。
语言设计者——或至少语言实现者——喜欢写能生成快代码的编译器。但我不觉得这是让一门语言对用户而言快起来的原因。Knuth(Donald Knuth,《计算机程序设计艺术》作者)很早以前就指出——速度只在少数关键瓶颈处有意义。而任何试过的人都知道——你猜不到这些瓶颈在哪儿。Profiler(性能剖析器)才是答案。
语言设计者在解错的问题。用户不需要让 benchmark 跑得快。他们需要的是一门能告诉他们’自己程序里哪部分需要重写’的语言。这才是实践中速度的来源。所以也许这样净算下来更划算——语言实现者把他们花在编译器优化上的时间分一半出来,去写一个好 profiler。
3. 你需要一个应用来推动语言设计。
这不一定是绝对的规则,但最好的语言似乎都和某个’它们正在被用来写’的应用一同演化。C 是被一群为系统编程需要它的人写出来的。Lisp 部分是为了做符号微分而开发的——McCarthy(John McCarthy,Lisp 之父)当年急于动手——他在 1960 年那篇关于 Lisp 的第一篇论文里就已经在写微分程序。
如果你的应用解决的是某个新问题,那特别好。这会推着你的语言长出程序员需要的新特性。我个人感兴趣的是写一门适合写服务器端应用的语言。
[panel 上 Guy Steele(Common Lisp 设计者之一,Java 规范联合作者)也提了这一点,并补充说——那个应用不应当是’写你这门语言的编译器’——除非你这门语言正好就是为写编译器而设计的。]
4. 一门语言必须适合写一次性程序。
你知道什么是一次性程序——你为某项有限任务很快写出来的东西。我觉得你环顾四周会发现——许多大型、严肃的程序最初都是一次性程序。我甚至不会意外多数程序都是从一次性程序开始的。所以——如果你想做一门普遍适合写软件的语言,它必须适合写一次性程序——因为那是大多数软件的幼虫期。
5. 语法和语义是相连的。
按传统,人们认为语法和语义是完全分开的两件事。这话听起来会很震撼——但它们也许并不完全分开。我觉得你想在你语言里要什么,可能与你怎样表达它有关。
我最近和 Robert Morris(PG 在 Viaweb 的合伙人)聊天,他指出——算子重载在中缀语法的语言里收益更大。在前缀语法的语言里,你定义的任何函数实际上就是一个算子。如果你想为自己捏出来的新数字类型定义一个加法,你只要定义一个新函数把它们加起来就好了。如果你在中缀语法的语言里这么做,’用一个被重载的算子’和’调用一个函数’在外观上有巨大差别。
IV. 趋势
1. 新编程语言。
回到 1970 年代,设计新编程语言是时髦的事。最近不再如此。但我觉得基于服务器的软件会让新语言重新时髦起来。基于服务器的软件让你想用什么语言就用什么——所以如果有人真的设计出一门看起来比现有可选项更好的语言,就会有人冒险去用它。
2. 分时(time-sharing)。
Richard Kelsey 在上一场 panel 里把这条作为“时机重新到来“的想法提了出来——我完全同意他。我的猜测(似乎也是 Microsoft 的猜测)是——大量计算会从桌面挪到远端服务器。换句话说——分时回来了。我觉得这件事在语言层面会需要支持。比如,我知道 Richard 和 Jonathan Rees 在 Scheme 48 里就进程调度做了大量工作。
3. 效率。
最近开始有种感觉——电脑终于够快了。我们越来越多地听到字节码(byte code)——这至少对我意味着——我们觉得有富裕的算力可花。但我不觉得到了基于服务器的软件那里我们还有富裕。总得有人付钱给跑这套软件的服务器——而每台机器能支撑的用户数,正是它资本成本的除数。
所以我觉得效率会重要——至少在计算瓶颈处。把 I/O 做快会尤其重要——因为基于服务器的应用做大量 I/O。
最终也许会发现字节码并不划算。Sun 和 Microsoft 此刻看起来正面对面打一场字节码之战。但他们这么做的原因,是字节码是个方便他们插进流程里的位置——而不是字节码本身是个好主意。也许整片战场最终会被绕过去——那会有点好笑。
V. 想推广的想法
1. 客户端。
这只是猜测——但我猜——对多数应用而言,胜出的模型会是纯粹基于服务器的。设计一套’假设人人都装了你的客户端’的软件,就像设计一个’假设人人都会诚实’的社会——当然方便,但你必须假设它永远不会发生。
我猜会有一大波带某种 Web 接入能力的设备涌出来——而你能假设的全部,是它们支持简单的 HTML 和表单。手机上会有浏览器吗?Palm Pilot 上会有电话吗?BlackBerry 的屏幕会变大吗?你能在 Game Boy 上浏览 Web 吗?手表上呢?我不知道——而如果我赌“一切都在服务器上“,我就不必知道。把所有脑子都放在服务器上,要稳健得多。
2. 面向对象编程。
我意识到这是个有争议的话题——但我不觉得面向对象编程有多大不了。它对某些种类的应用——那些正好需要那种特定数据结构的——比如窗口系统、仿真、CAD 程序——是个不错的模型。但我看不出它为什么应该是所有编程的模型。
我觉得大公司里的人喜欢面向对象编程的部分原因,是它能产出一大堆看上去像工作的东西。某个本来自然地表示为整数列表的东西,现在可以被表示成一个类——配上各种脚手架、又是叫又是嚷。
面向对象编程的另一个吸引力是——方法(method)给了你一些一等公民函数的效果。但这对 Lisp 程序员是老消息了。当你有真正的一等公民函数时,你可以按手头任务最适合的方式直接用它们——而不必把一切都塞进“类与方法“的模具里。
我觉得——这件事对语言设计的意涵是——你不该把面向对象编程做得太深嵌。也许答案是——提供更通用的、底层的东西,让人们想要什么对象系统就以库的形式自己去设计。
3. 委员会式设计。
让委员会来设计你的语言是个大坑——而且不只是因为大家都知道的那些理由。大家都知道委员会倾向于产出疙疙瘩瘩、不一致的设计。但我觉得更大的危险是——他们不会冒险。当一个人说了算时,他可以做出委员会永远不会同意的冒险决策。
但是——设计一门好语言真的需要冒险吗?许多人也许会怀疑——语言设计是那种应该相当贴着惯例智慧走的事。我打赌事实并非如此。在人做的其他每件事上,回报与风险成比例——为什么语言设计会例外?