Loading...

关于语言设计的五个问题

Original

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 的开发部分是为了进行符号微分,而麦卡锡是如此渴望开始,以至于他在 1960 年的第一篇关于 Lisp 的论文中就编写了微分程序。

如果您的应用程序解决了一些新问题,那就更好了。这将促使您的语言具有程序员需要的新功能。我个人对编写一种适合编写基于服务器的应用程序的语言很感兴趣。

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

4. 一种语言必须适合编写一次性程序。

你知道一次性程序是什么:你为了完成一些有限的任务而快速编写的程序。我想如果你环顾四周,你会发现很多大型、严肃的程序都是从一次性程序开始的。如果大多数程序都是从一次性程序开始的,我一点也不惊讶。所以如果你想开发一种适合编写一般软件的语言,它就必须适合编写一次性程序,因为那是大多数软件的幼年阶段。

5.句法与语义相关。

传统上,人们认为语法和语义是完全分开的。这听起来可能令人震惊,但事实可能并非如此。我认为,你想要用你的语言表达什么可能与你如何表达它有关。

我最近和罗伯特·莫里斯交谈过,他指出运算符重载在具有中缀语法的语言中是一个更大的优势。在具有前缀语法的语言中,您定义的任何函数实际上都是运算符。如果您想为自己编造的新类型的数字定义加号,您可以定义一个新函数来添加它们。如果您在具有中缀语法的语言中这样做,重载运算符的使用和函数调用在外观上有很大区别。

1. 新的编程语言。

早在 1970 年代,设计新的编程语言就是一种时尚。但最近却不再流行了。但我认为基于服务器的软件将使新语言再次流行起来。有了基于服务器的软件,你可以使用任何你想要的语言,所以如果有人设计出一种看起来比其他语言更好的语言,就会有人冒险使用它。

2. 分时。

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

3.效率。

最近,人们开始觉得计算机终于够快了。我们开始越来越多地听到字节码,这至少对我来说意味着我们觉得我们有空闲的周期。但我认为,对于基于服务器的软件,我们不会这样做。有人将不得不为运行软件的服务器付费,而他们每台机器可以支持的用户数量将是其资本成本的除数。

所以我认为效率很重要,至少在计算瓶颈方面。快速执行 i/o 尤其重要,因为基于服务器的应用程序执行大量 i/o。

最终,字节码可能不是胜利者。目前,Sun 和 Microsoft 似乎在字节码之战中正面交锋。但他们之所以这么做,是因为字节码是方便他们插入进程的地方,而不是因为字节码本身是个好主意。最终,整个战场可能会被绕过。那会很有趣。

1. 客户。

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

我认为,具有某种 Web 访问权限的设备将大量涌现,而您所能想到的只是它们能够支持简单的 HTML 和表单。您的手机上会有浏览器吗?您的掌上电脑上会有电话吗?您的黑莓手机会配备更大的屏幕吗?您能在 Gameboy 上浏览网页吗?您的手表呢?我不知道。如果我打赌所有东西都在服务器上,我不需要知道。将所有大脑都放在服务器上会更加强大。

2.面向对象编程。

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

我认为大公司的人喜欢面向对象编程的部分原因是因为它可以产生很多看似工作量的东西。某些东西可能自然地被表示为整数列表,现在可以被表示为一个带有各种脚手架和繁琐工作的类。

面向对象编程的另一个吸引力在于,方法可以给你带来一些一流函数的效果。但这对于 Lisp 程序员来说已经是老生常谈了。当你拥有真正的一流函数时,你可以按照适合手头任务的任何方式使用它们,而不是将所有内容强行塞入类和方法的模型中。

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

3. 由委员会设计。

让委员会来设计你的语言是一个很大的陷阱,不仅仅是因为众所周知的原因。每个人都知道委员会往往会产生不连贯、不一致的设计。但我认为更大的危险是他们不愿意冒险。当一个人负责时,他可以冒委员会永远不会同意的风险。

但是,设计一门好的语言是否需要冒险呢?很多人可能会怀疑语言设计应该遵循传统观点。我敢打赌这不是真的。在人们所做的其他所有事情中,回报与风险成正比。语言设计为什么应该有所不同呢?