关于语言设计的五个问题
Original2001年5月
(这些是我为2001年5月10日在麻省理工学院举行的一个关于编程语言设计的小组讨论所做的一些笔记。)
1. 编程语言是为人而设的。
编程语言是人类与计算机交流的方式。计算机可以使用任何无歧义的语言来交流。我们使用高级语言的原因是因为人类无法处理机器语言。编程语言的目的是防止我们脆弱的大脑被大量细节所淹没。
建筑师知道,有些设计问题比其他问题更具个人性。设计桥梁是最干净、最抽象的设计问题之一。在这里,你的工作主要是用最少的材料跨越一定的距离。设计椅子则是另一个极端。椅子设计师必须花时间考虑人类的屁股。
软件也存在这种差异。设计用于路由网络数据的算法是一个很好的抽象问题,就像设计桥梁一样。而设计编程语言就像设计椅子:这完全是关于应对人类的弱点。
我们大多数人都不愿意承认这一点。设计数学优雅的系统听起来要比迎合人类弱点更有吸引力。优雅确实有一定作用:某些优雅可以使程序更易于理解。但优雅不应该成为目的本身。
当我说语言必须被设计来适应人类的弱点时,我并不是说语言必须为糟糕的程序员而设计。事实上,我认为应该为最优秀的程序员设计。但即使是最优秀的程序员也有局限性。我不认为任何人会喜欢在一种所有变量都是带整数下标的字母x的语言中编程。
2. 为自己和朋友而设计。
如果你看一下编程语言的历史,你会发现很多最好的语言都是为它们的作者自己设计的,而很多最糟糕的语言都是为其他人设计的。
当语言是为其他人设计时,这总是针对一个特定的群体:比语言设计者聪明的人。所以你得到的是一种对你说教的语言。COBOL是最极端的例子,但很多语言都充满了这种精神。
这与语言的抽象程度无关。C很低级,但它是为它的作者设计的,这就是为什么黑客喜欢它。
为糟糕的程序员设计语言的论点是,糟糕的程序员比优秀的程序员多。这可能是真的。但那些少数优秀的程序员编写了大量的软件。
我对这个问题感兴趣:如何设计一种最优秀的黑客都会喜欢的语言?我恰好认为这与如何设计一种好的编程语言的问题是一样的,但即使不是,这也是一个有趣的问题。
3. 尽可能给程序员更多的控制权。
许多语言(特别是为其他人设计的语言)都有保姆的态度:他们试图阻止你做他们认为不好的事情。我喜欢相反的方法:尽可能多地给程序员控制权。
当我第一次学习Lisp时,我最喜欢的是它把我当作平等的伙伴。在我之前学习的其他语言中,语言和我的程序是非常分离的。但在Lisp中,我编写的函数和宏就像构成语言本身的那些一样。如果我想的话,我可以重写整个语言。这就像开源软件一样有吸引力。
4. 追求简洁。
简洁被低估甚至被鄙视。但如果你深入了解黑客的内心,你会发现他们真的很喜欢它。你有多少次听到黑客们怀念在APL中,他们只用几行代码就能做出惊人的事情?我认为,真正聪明的人真正喜欢的任何东西都值得关注。
我认为,你能做的任何事情来缩短程序都是好的。应该有大量的库函数;任何可以隐式的东西都应该隐式;语法应该简洁到一种过度的地步;甚至事物的名称也应该简短。
不仅程序应该简短,手册也应该简单。手册的很大一部分都是用于澄清、保留、警告和特殊情况。如果你强迫自己缩短手册,在最好的情况下,你是通过修复语言中需要如此多解释的东西来实现的。
5. 承认什么是黑客。
很多人希望黑客工作是数学,或者至少是一种自然科学。我认为黑客更像是建筑。建筑与物理学有关,因为建筑师必须设计不会倒塌的建筑,但建筑师的实际目标是建造出色的建筑,而不是做关于静力学的发现。
黑客喜欢做的是编写出色的程序。我认为,至少在我们自己的心目中,我们必须记住编写出色的程序是一件值得尊敬的事情,即使这项工作无法轻易转化为发表研究论文这种传统的智力货币。从智力上来说,设计一种程序员会喜欢的语言,与设计一种可以发表论文的可怕语言一样有价值。
1. 如何组织大型库?
库正变得越来越重要的编程语言组件。它们也越来越大,这可能很危险。如果找到能做你想要的事情的库函数比自己编写还要长,那么所有这些代码都只是在让你的手册变厚。(Symbolics手册就是一个很好的例子。)所以我认为我们必须努力组织库。理想情况下,设计它们的方式是,程序员可以猜到哪个库调用会做正确的事情。
2. 人们真的害怕前缀语法吗?
这是一个开放性问题,因为我已经思考了多年,但仍然不知道答案。前缀语法对我来说似乎很自然,但可能除了数学之外。不过,Lisp的不受欢迎可能只是因为它有一个陌生的语法。如果这是真的,是否要采取任何行动,这是另一个问题。
3. 服务器端软件需要什么?
我认为在未来二十年内,最令人兴奋的新应用程序将是基于Web的应用程序,也就是说坐落在服务器上并通过Web浏览器与您对话的程序。为了编写这类程序,我们可能需要一些新的东西。
我们需要的一件事是对新的服务器端应用程序发布方式的支持。与桌面软件一年两次的大版本发布不同,服务器端应用程序是以一系列小的变更发布的。您可能一天有5到10次发布。而且通常每个人都会使用最新版本。
您知道如何设计可调试的程序吗?同样地,服务器端软件也必须设计成可变更的。您必须能够轻易地进行更改,或至少知道什么是小的变更,什么是重大变更。
另一个可能对服务器端软件有用的东西,令人惊讶的是,是延续性(continuations)。在基于Web的软件中,您可以使用类似延续传递风格(continuation-passing style)的方法,在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和微软似乎正在进行一场字节码之战。但他们这样做是因为字节码是一个方便的切入点,而不是因为字节码本身是一个好主意。这整个战场可能会被绕过去,那将是很有趣的。
1. 客户端。
这只是一个猜测,但我猜大多数应用程序的获胜模式将是纯服务器端。设计软件时假设每个人都会有你的客户端,就像设计一个社会时假设每个人都诚实一样。这当然很方便,但你必须假设这永远不会发生。
我认为将会出现大量具有某种Web访问功能的设备,你唯一可以假设的就是它们能支持简单的HTML和表单。你的手机上会有浏览器吗?你的掌上电脑会有电话吗?你的黑莓手机会有更大的屏幕吗?你能在游戏机或手表上浏览网页吗?我不知道。如果我把所有东西都放在服务器上,我就不需要知道这些。把所有的"大脑"都放在服务器上要强大得多。
2. 面向对象编程。
我知道这是一个有争议的观点,但我不认为面向对象编程有那么大的意义。我认为它是一种适合某些需要特定数据结构的应用程序的模型,比如窗口系统、模拟和CAD程序。但我不明白为什么它应该成为所有编程的模型。
我认为大公司喜欢面向对象编程的一个原因是,它可以产生大量看起来像工作的东西。一些本来可以用整数列表表示的东西,现在可以用一个带有各种脚手架和繁忙活动的类来表示。
面向对象编程的另一个吸引力是,方法可以给你一些一等函数的效果。但对于Lisp程序员来说,这已经是老新闻了。当你有真正的一等函数时,你可以根据手头的任务以适当的方式使用它们,而不是把所有东西都强行塞进类和方法的模具。
这对语言设计意味着什么,我认为,就是你不应该过于深入地构建面向对象编程。也许答案是提供更通用、更基础的东西,让人们自己设计他们想要的任何对象系统作为库。
3. 委员会设计。
让你的语言由委员会设计是一个很大的陷阱,不仅仅是因为大家都知道的原因。大家都知道,委员会倾向于产生不平整、不一致的设计。但我认为更大的危险是,他们不会冒险。当一个人负责时,他可以做出委员会永远不会同意的冒险。
语言设计是否需要冒险呢?很多人可能会怀疑,语言设计是一个应该紧跟传统智慧的领域。我打赌这不是真的。在其他所有事情中,回报与风险成正比。为什么语言设计就不应该如此呢?