关于语言设计的五个问题

2001年5月

(这些是我在2001年5月10日在麻省理工学院关于编程语言设计的座谈会上做的一些笔记。)

1. 编程语言是为人服务的。

编程语言是人与计算机交流的方式。计算机很乐意使用任何一种无歧义的语言。我们之所以有高级语言,是因为人们无法处理机器语言。编程语言的意义在于防止我们脆弱的人脑被大量的细节所淹没。

建筑师知道,某些类型的设计问题比其他问题更具个人性。最干净、最抽象的设计问题之一是设计桥梁。在那里,你的工作主要是在给定的距离上用最少的材料进行跨越。另一个极端是设计椅子。椅子设计师必须花时间思考人类的屁股。

软件的变化方式相同。设计用于通过网络路由数据的算法是一个不错的抽象问题,就像设计桥梁一样。而设计编程语言就像设计椅子:一切都是为了应对人类的弱点。

我们大多数人都不愿意承认这一点。设计具有高度数学优雅性的系统听起来比迎合人类的弱点更吸引我们。数学上的优雅确实有其作用:某些优雅可以使程序更容易理解。但优雅本身并不是目的。

当我说语言必须设计成适合人类的弱点时,我并不是说语言必须为糟糕的程序员而设计。事实上,我认为你应该为最好的程序员设计,但即使是最好的程序员也有局限性。我不认为有人会喜欢用一种所有变量都是带有整数下标的字母 x 的语言进行编程。

2. 为你自己和你的朋友设计。

如果你看看编程语言的历史,你会发现很多最好的语言都是为自己的作者设计的,而很多最糟糕的语言都是为其他人设计的。

当语言是为其他人设计的时候,它总是针对一个特定的群体:不如语言设计师聪明的人。所以你会得到一种居高临下的语言。Cobol 是最极端的例子,但很多语言都弥漫着这种精神。

这与语言的抽象程度无关。C 语言相当底层,但它是为作者设计的,这就是为什么黑客喜欢它。

为糟糕的程序员设计语言的理由是,糟糕的程序员比优秀的程序员多。这可能是事实。但少数优秀的程序员编写了不成比例的大量软件。

我对这个问题很感兴趣:如何设计一种最好的黑客会喜欢的语言?我碰巧认为这与如何设计一种好的编程语言是相同的,但即使不是,至少也是一个有趣的问题。

3. 尽可能多地给予程序员控制权。

许多语言(尤其是为其他人设计的语言)都抱着一种家庭教师的态度:它们试图阻止你做他们认为对你没有好处的事情。我喜欢相反的方法:尽可能多地给予程序员控制权。

当我第一次学习 Lisp 时,我最喜欢它的一点是它认为我是一个平等的伙伴。在我之前学过的其他语言中,有语言和我的程序(用该语言编写),两者非常分离。但在 Lisp 中,我编写的函数和宏就像构成语言本身的那些函数和宏一样。如果我愿意,我可以重写语言。它具有与开源软件相同的吸引力。

4. 力求简洁。

简洁被低估甚至被鄙视。但如果你深入了解黑客的内心,你会发现他们真的很喜欢它。你听过多少次黑客津津乐道地说,在 APL 中,他们可以用几行代码完成令人惊叹的事情?我认为任何聪明人真正喜欢的东西都值得关注。

我认为几乎所有可以使程序更短的方法都是好的。应该有很多库函数;任何可以隐式的东西都应该隐式;语法应该简洁到极致;甚至事物的名称也应该简短。

而且不仅程序应该简短。手册也应该很薄。手册的很大一部分被澄清、保留、警告和特殊情况所占据。如果你强迫自己缩短手册,在最好的情况下,你可以通过修复语言中需要大量解释的东西来做到这一点。

5. 承认什么是黑客行为。

很多人希望黑客行为是数学,或者至少是像自然科学一样的东西。我认为黑客行为更像建筑。建筑与物理学相关,因为建筑师必须设计不会倒塌的建筑物,但建筑师的实际目标是建造伟大的建筑物,而不是发现关于静力学的知识。

黑客喜欢做的是制作伟大的程序。我认为,至少在我们自己的脑海中,我们必须记住,编写伟大的程序是一件值得称赞的事情,即使这项工作不容易转化为研究论文的传统知识货币。从智力上讲,设计一种程序员会喜欢的语言与设计一种体现你可以发表论文的某些想法的可怕语言一样有价值。

1. 如何组织大型库?

库正成为编程语言中越来越重要的组成部分。它们也变得越来越大,这可能很危险。如果找到可以完成你想要功能的库函数所花费的时间比你自己编写它所花费的时间更长,那么所有这些代码都无济于事,只会使你的手册变厚。(Symbolics 手册就是一个例子。)所以我认为我们将不得不努力寻找组织库的方法。理想的情况是设计它们,以便程序员可以猜测哪个库调用会做正确的事情。

2. 人们真的害怕前缀语法吗?

这是一个开放的问题,因为我已经思考了很多年,但仍然不知道答案。除了数学之外,前缀语法对我来说似乎很自然。但 Lisp 的不受欢迎可能仅仅是因为它具有不熟悉的语法。如果这是真的,是否要对此做任何事情是另一个问题。

3. 基于服务器的软件需要什么?

我认为未来二十年内编写的许多最令人兴奋的新应用程序将是基于 Web 的应用程序,这意味着位于服务器上并通过 Web 浏览器与你对话的程序。要编写这些类型的程序,我们可能需要一些新的东西。

我们需要的一件事是支持基于服务器的应用程序发布的新方式。与桌面软件一年发布一两个大型版本不同,基于服务器的应用程序以一系列小的更改发布。你可能每天有多达五到十个版本。通常,每个人都将始终使用最新版本。

你知道如何设计可调试的程序吗?同样,基于服务器的软件也必须设计成可更改的。你必须能够轻松地更改它,或者至少知道什么是小的更改,什么是重大的更改。

令人惊讶的是,对于基于服务器的软件,另一件可能被证明有用的东西是延续。在基于 Web 的软件中,你可以使用类似于延续传递样式的东西来获得 Web 会话的固有无状态世界中子例程的效果。如果不太昂贵,那么拥有实际的延续可能是有价值的。

4. 还剩下哪些新的抽象概念有待发现?

我不确定这个希望有多合理,但我个人真正想做的一件事是发现一种新的抽象——一种可以像拥有一流函数或递归甚至关键字参数一样产生重大影响的东西。这可能是一个不可能的梦想。这些东西不会经常被发现。但我一直在寻找。

1. 你可以使用任何你想要的语言。

编写应用程序过去意味着编写桌面软件。在桌面软件中,存在一种很大的偏见,即使用与操作系统相同的语言编写应用程序。因此,十年前,编写软件几乎意味着用 C 语言编写软件。最终,一种传统演变而来:应用程序不得用不寻常的语言编写。这种传统发展了很长时间,以至于像经理和风险投资家这样的非技术人员也学会了它。

基于服务器的软件打破了整个模型。使用基于服务器的软件,你可以使用任何你想要的语言。几乎没有人理解这一点(尤其是不理解经理和风险投资家)。少数黑客理解这一点,这就是为什么我们甚至听说过像 Perl 和 Python 这样的新的、独立的语言。我们听说 Perl 和 Python 并不是因为人们使用它们来编写 Windows 应用程序。

对于我们这些对设计编程语言感兴趣的人来说,这意味着现在可能存在我们工作的实际受众。

2. 速度来自分析器。

语言设计者,或者至少是语言实现者,喜欢编写生成快速代码的编译器。但我不认为这是使用户语言快速的原因。Knuth 很久以前就指出,速度只在少数关键瓶颈中才重要。任何尝试过的人都知道你无法猜测这些瓶颈在哪里。分析器是答案。

语言设计者正在解决错误的问题。用户不需要基准测试来快速运行。他们需要的是一种可以向他们展示自己程序的哪些部分需要重写的语言。这就是速度在实践中的来源。因此,如果语言实现者将他们原本用于编译器优化的一半时间用于编写一个好的分析器,那么这可能是一个净胜。

3. 你需要一个应用程序来驱动语言的设计。

这可能不是一个绝对的规则,但似乎最好的语言都是与它们被用来编写的某些应用程序一起发展起来的。C 语言是由需要它进行系统编程的人编写的。Lisp 的开发部分是为了进行符号微分,McCarthy 非常渴望开始,以至于他在 1960 年关于 Lisp 的第一篇论文中就已经在编写微分程序了。

如果你的应用程序解决了某些新问题,那就特别好。这将倾向于推动你的语言具有程序员需要的新功能。我个人有兴趣编写一种适合编写基于服务器的应用程序的语言。

[在小组讨论中,Guy Steele 也提出了这一点,并补充建议,除非你的语言恰好用于编写编译器,否则该应用程序不应包括编写你的语言的编译器。]

4. 一种语言必须擅长编写一次性程序。

你知道什么是一次性程序:你为某些有限的任务快速编写的东西。我认为如果你环顾四周,你会发现很多大型、严肃的程序都是从一次性程序开始的。如果_大多数_程序都是从一次性程序开始的,我不会感到惊讶。因此,如果你想制作一种擅长编写通用软件的语言,它必须擅长编写一次性程序,因为这是一次性程序是大多数软件的幼虫阶段。

5. 语法与语义相关。

传统上认为语法和语义是完全分离的。这听起来会令人震惊,但它们可能并非如此。我认为你希望在你的语言中得到的东西可能与你表达它的方式有关。

我最近在和 Robert Morris 谈话,他指出运算符重载在具有中缀语法的语言中是一个更大的胜利。在具有前缀语法的语言中,你定义的任何函数实际上都是一个运算符。如果你想为你创建的一种新型数字定义一个加号,你可以只定义一个新函数来添加它们。如果你在具有中缀语法的语言中这样做,那么重载运算符的使用和函数调用在外观上存在很大差异。

1. 新的编程语言。

早在 20 世纪 70 年代,设计新的编程语言就很流行。最近一直没有。但我认为基于服务器的软件将使新语言再次流行起来。使用基于服务器的软件,你可以使用任何你想要的语言,因此如果有人确实设计了一种实际上比其他可用语言更好的语言,那么就会有人冒险使用它。

2. 分时。

Richard Kelsey 在上次的小组讨论中提出了这个想法,认为它的时代已经再次到来,我完全同意他的观点。我的猜测(以及微软的猜测,似乎)是,许多计算将从桌面转移到远程服务器上。换句话说,分时又回来了。我认为需要在语言级别上支持它。例如,我知道 Richard 和 Jonathan Rees 在 Scheme 48 中实现进程调度方面做了很多工作。

3. 效率。

最近,计算机似乎终于足够快了。我们越来越开始听到字节码的消息,至少对我来说,这意味着我们觉得我们有周期可以节省。但我不认为我们会这样,使用基于服务器的软件。有人必须为运行软件的服务器付费,并且每台机器可以支持的用户数量将是其资本成本的除数。

因此,我认为效率很重要,至少在计算瓶颈中很重要。快速执行 i/o 特别重要,因为基于服务器的应用程序会执行大量 i/o。

最终,字节码可能不是一个胜利。Sun 和 Microsoft 目前似乎正在进行一场字节码之战。但他们这样做是因为字节码是一个方便的将自己插入到流程中的地方,而不是因为字节码本身就是一个好主意。最终,整个战场可能会被绕过。那会有点好笑。

1. 客户端。

这只是一个猜测,但我的猜测是,大多数应用程序的获胜模式将是纯粹基于服务器的。设计在每个人都有你的客户端的假设下工作的软件就像设计一个在每个人都诚实的假设下工作的社会一样。这当然很方便,但你必须假设它永远不会发生。

我认为会有大量具有某种 Web 访问权限的设备激增,你唯一能假设的是它们可以支持简单的 html 和表单。你的手机上会有浏览器吗?你的掌上电脑里会有电话吗?你的黑莓会获得更大的屏幕吗?你可以在你的 gameboy 上浏览网页吗?你的手表?我不知道。如果我打赌一切都在服务器上,我就不必知道。让所有的大脑都在服务器上要健壮得多。

2. 面向对象编程。

我知道这是一个有争议的问题,但我不认为面向对象编程是那么重要的事情。我认为对于某些需要那种特定类型的数据结构的应用程序来说,它是一个很好的模型,例如窗口系统、模拟和 cad 程序。但我不明白为什么它应该是所有编程的模型。

我认为大型公司的人喜欢面向对象编程的部分原因是它产生了大量看起来像工作的东西。现在,可以用各种脚手架和喧嚣来表示自然表示为整数列表的东西。

面向对象编程的另一个吸引力在于,方法为你提供了一流函数的一些效果。但这对 Lisp 程序员来说已经是旧闻了。当你拥有实际的一流函数时,你可以以适合手头任务的任何方式使用它们,而不是将所有内容强制放入类和方法的模具中。

我认为,对于语言设计来说,这意味着你不应该将面向对象编程构建得太深入。也许答案是提供更通用的、底层的东西,让人们将他们想要的任何对象系统设计为库。

3. 委员会设计。

让委员会设计你的语言是一个很大的陷阱,而不仅仅是因为每个人都知道的原因。每个人都知道委员会倾向于产生块状的、不一致的设计。但我认为更大的危险是他们不会冒险。当一个人负责时,他可以承担委员会永远不会同意的风险。

但设计一种好的语言是否需要冒险呢?许多人可能会怀疑语言设计是你应该相当接近传统智慧的事情。我敢打赌这不是真的。在人们所做的其他一切事情中,回报与风险成正比。为什么语言设计会有什么不同?