如何获得广泛关注
Original2001年5月
(这篇文章被写成一种针对新编程语言的商业计划。所以它遗漏了(因为它认为理所当然)一个优秀编程语言最重要的特征:非常强大的抽象能力。)
我的一位朋友曾经告诉一位著名的操作系统专家,他想设计一种非常好的编程语言。那位专家告诉他这样做是浪费时间,编程语言的流行程度并不取决于它们的优点,所以无论他的语言有多好,都不会有人使用它。至少,这就是他设计的语言所遭遇的命运。
什么才能使一种语言变得流行呢?流行语言是否真的值得它们的流行地位?值得去定义一种好的编程语言吗?如何去做呢?
我认为这些问题的答案可以通过观察黑客,并了解他们的需求来找到。编程语言是为黑客而设计的,一种编程语言之所以是一种好的编程语言(而不仅仅是一种形式化语义或编译器设计的练习),是因为黑客喜欢它。
1 流行性的机制
确实,大多数人选择编程语言并不完全基于它们的优点。大多数程序员都是被别人告知要使用哪种语言。然而,我认为这些外部因素对编程语言流行程度的影响并没有那么大。我认为更大的问题是,黑客心目中的好编程语言与大多数语言设计师的看法是不一样的。
在这两者之间,黑客的意见才是真正重要的。编程语言不是定理,而是为人而设计的工具,其设计必须适应人类的长处和弱点,就像鞋子必须适合人类的脚一样。如果一双鞋穿起来会夹脚,那就是一双糟糕的鞋子,无论它作为一件雕塑作品有多优雅。
也许大多数程序员无法分辨一种好语言和一种坏语言。但是这与其他任何工具也没有什么不同。这并不意味着努力设计一种好的语言是浪费时间。专业黑客能够一眼识别一种好语言,并会使用它。专业黑客只是少数,但这些少数人编写了所有优秀的软件,他们的影响力如此之大,以至于其他程序员也倾向于使用他们使用的语言。事实上,很多时候这不仅仅是影响力,也是命令:这些专业黑客作为老板或院长,会告诉其他程序员使用什么语言。
专业黑客的意见并不是决定编程语言相对流行程度的唯一力量 - 遗留软件(COBOL)和炒作(Ada、Java)也起作用 - 但我认为它是最强大的力量,从长远来看。只要有初始的临界质量,并有足够的时间,一种编程语言可能会变得大约和它应得的一样流行。而且流行进一步区分了好语言和坏语言,因为来自真实用户的反馈总是会导致改进。看看任何流行语言在其生命周期中的变化就知道了。Perl和Fortran是极端情况,但即使是Lisp也发生了很大变化。例如,Lisp 1.5没有宏,这些是后来在麻省理工学院的黑客使用Lisp编写实际程序后才发展出来的。[1]
所以,无论一种语言是否必须好才会流行,我认为一种语言必须流行才会变好。而且它必须保持流行才能保持优秀。编程语言领域的技术并不停滞不前。然而,我们今天使用的Lisp基本上还是20世纪80年代中期麻省理工学院那个时候的样子,因为那是Lisp最后一次拥有足够大且要求严格的用户群。
当然,黑客必须知道一种语言的存在才能使用它。他们从哪里得知?从其他黑客那里。但是必须有一群初始的黑客在使用这种语言,其他人才能听说它的存在。我想知道这个群体需要有多大?需要多少用户才算一个临界质量?我脑海中的估计是二十个。如果一种语言有二十个独立的用户,也就是说二十个人自己决定使用它,我就认为它是真实存在的。
要达到这一点一定不容易。我不会感到惊讶,要从零到二十可能比从二十到一千还要难。获得这些初始二十个用户的最佳方式可能是使用特洛伊木马:提供人们需要的应用程序,恰好是用新语言编写的。
2 外部因素
让我们首先承认一个确实影响编程语言流行程度的外部因素。要想变得流行,一种编程语言必须成为某个流行系统的脚本语言。Fortran和COBOL是早期IBM主机的脚本语言。C是Unix的脚本语言,后来Perl也成为了其脚本语言。Tcl是Tk的脚本语言。Java和JavaScript被打算作为Web浏览器的脚本语言。
Lisp并不是一种广泛流行的语言,因为它不是任何广泛流行系统的脚本语言。它保留下来的一些流行程度可以追溯到1960年代和1970年代,当时它是麻省理工学院的脚本语言。当时许多伟大的程序员都与麻省理工学院有某种关联。在20世纪70年代早期,即C语言出现之前,麻省理工学院的Lisp方言MacLisp是一个认真的黑客想使用的为数不多的编程语言之一。
如今,Lisp是两个中等流行的系统Emacs和AutoCAD的脚本语言,因此我猜测如今大部分Lisp编程都是在Emacs Lisp或AutoLisp中进行的。
编程语言并不孤立地存在。"黑客"是一个及物动词 - 黑客通常是在黑某件东西 - 实际上语言是相对于它们用来黑的东西而被评判的。所以如果你想设计一种流行的语言,你要么必须提供比仅仅一种语言更多的东西,要么就必须设计你的语言来取代某个现有系统的脚本语言。
Common Lisp的不受欢迎部分原因是它是个孤儿。它最初是配有一个可以 hack 的系统:Lisp机器。但是Lisp机器(以及并行计算机)在1980年代被通用处理器日益增强的能力所碾压。如果Common Lisp能成为Unix的良好脚本语言,它也许仍会保持受欢迎。遗憾的是,它是一种可怕的糟糕脚本语言。
这种情况的一种描述方式是说,一种语言不是以自身的优点来评判的。另一种观点是,一种编程语言除非它也是某些东西的脚本语言,否则它就不算是一种编程语言。这种说法似乎不公平,但如果这是个惊喜的话。我认为它不比期望一种编程语言有一个实现更不公平。这只是编程语言的一部分特征。
一种编程语言当然需要一个良好的实现,而且这个实现必须是免费的。公司会为软件付费,但个人黑客不会,而你需要吸引的正是这些黑客。
一种语言还需要有关于它的一本书。这本书应该精简、写得好,并且充满良好的例子。K&R就是这样的理想。现在我几乎可以说,一种语言必须有一本O'Reilly出版的书。这已经成为一种语言受到黑客重视的测试标准。
此外还需要有在线文档。事实上,这本书可以从在线文档开始。但我不认为实体书已经过时了。它们的格式很方便,而由出版商施加的事实上的审查机制也是一个有用的(虽然不太完美)的过滤器。书店是学习新语言的一个非常重要的场所。
3 简洁
既然你可以提供任何语言所需的三件事 — 一个免费实现、一本书以及一些可以 hack 的东西 — 你如何设计一种黑客会喜欢的语言?
黑客喜欢的一个特点是简洁。黑客们很懒惰,就像数学家和现代建筑师一样懒惰:他们讨厌任何多余的东西。如果说黑客在编写程序之前,至少在潜意识里,会根据他需要输入的总字符数来决定使用什么语言,那也并不太离谱。如果这不完全符合黑客的思维方式,那么语言设计者也应该按照这种方式来行事。
试图用冗长的类似英语的表达式来呵护用户是错误的。Cobol就有这种缺陷。一个黑客会认为被要求写
add x to y giving z
而不是
z = x+y
这不仅是对他智力的侮辱,也是对上帝的罪过。
有人曾经说过,Lisp应该使用first和rest而不是car和cdr,因为这样会使程序更容易阅读。也许前几个小时是这样。但一个黑客很快就会学会car意味着列表的第一个元素,cdr意味着其余部分。使用first和rest意味着要多打50%的字。而且它们的长度也不同,这意味着当它们在连续的行中被调用时,参数不会对齐,就像car和cdr经常做的那样。我发现代码在页面上的对齐方式对我来说很重要。当Lisp代码设置为等宽字体时,我几乎无法阅读,朋友们也说对其他语言也是如此。
简洁是强类型语言不擅长的一个领域。除了其他因素相同,没有人想在程序开头写一堆声明。任何可以隐含的东西都应该被隐含。
单个词元也应该很短。Perl和Common Lisp在这个问题上处于对立面。Perl程序可以是几乎令人费解的密集,而Common Lisp内置操作符的名称则可笑地很长。Common Lisp的设计者可能希望用户有文本编辑器可以为他们输入这些长名称。但长名称的代价不仅仅是打字的代价。还有阅读它的代价,以及它在屏幕上占用的空间的代价。
4 可破解性
对于黑客来说,有一件事比简洁更重要:能够做你想做的事。在编程语言的历史上,有相当大的努力致力于阻止程序员做一些被认为不恰当的事情。这是一个危险的自负计划。语言设计者怎么能知道程序员将需要做什么?我认为语言设计者最好将他们的目标用户视为一个天才,他将需要做一些他们从未设想过的事情,而不是一个需要被保护的笨蛋。这个笨蛋无论如何都会射中自己的脚。你可能会阻止他引用另一个包中的变量,但你不能阻止他编写一个设计糟糕的程序来解决错误的问题,并花费很长时间来完成它。
优秀的程序员常常想做一些危险和不体面的事情。我所说的不体面是指那些绕过语言试图呈现的任何语义外观的事情:例如,获取某个高级抽象的内部表示。黑客喜欢 hack,而 hack 意味着进入事物内部并二次猜测原始设计者。
*让自己被二次猜测。*当你制作任何工具时,人们会用你未曾预料的方式使用它,这对于一个高度细化的工具(如编程语言)尤其如此。许多黑客都想调整你的语义模型,而这是你从未想过的。我说,让他们这样做吧;给程序员尽可能多地访问内部资料的权限,只要不会危及诸如垃圾收集器之类的运行时系统。
在Common Lisp中,我经常想遍历一个结构的字段 — 例如,梳理对一个已删除对象的引用,或者找到未初始化的字段。我知道结构体在底层只是向量。然而,我不能编写一个可以在任何结构体上调用的通用函数。我只能按名称访问字段,因为这就是结构体应该表达的意思。
一个黑客可能在一个大程序中只需要曲解目标模型一两次。但这种能力带来的差异是巨大的。这可能不仅仅是为了解决一个问题。这里还有一种快感存在。黑客分享外科医生在粗糙内脏中摸索的秘密快感,青少年挤痘痘的秘密快感。[2] 对于男孩来说,某些类型的恐怖是令人着迷的。Maxim杂志每年都会出版一本照片集,其中包含了美女和惨烈事故的混合。他们知道自己的受众。
[1]
[2] 这个比喻似乎不太恰当。作为一个成熟的黑客,我相信你会寻找更优雅和中性的方式来表达这种快感。
在历史上,Lisp 一直擅长让黑客们自由发挥。Common Lisp 的政治正确性是一种异常。早期的 Lisp 让你能触碰到一切。幸运的是,这种精神在宏中被很好地保留下来了。作为一种可以对源代码进行任意转换的能力,这真是太好了。
经典的宏是真正黑客的工具 - 简单、强大且危险。理解它们的工作原理是如此简单:你调用一个函数来处理宏的参数,然后把它返回的结果插入到宏调用的位置。而卫生宏体现了相反的原则。它们试图保护你不去理解它们在做什么。我从未听说过有人能用一句话解释清楚卫生宏。它们是决定程序员应该想要什么的危险典范。卫生宏旨在保护我免受变量捕获等问题,但变量捕获恰恰是我在某些宏中想要的。
一个真正优秀的编程语言应该既洁净又肮脏:设计上很清晰,有一个小型的、理解良好且高度正交的操作符核心,但在让黑客们为所欲为的方面又很"肮脏"。C 语言就是这样。早期的 Lisp 也是如此。一种真正黑客语言总会有一些轻狂的特质。
一种好的编程语言应该具有一些特性,会让那些使用"软件工程"这个短语的人摇头表示不赞同。而在另一头,Ada 和 Pascal 这样的语言则是典范的正当性,适合教学用途,别无他用。
5. 即弃程序
要吸引黑客,一种语言必须善于编写黑客们想要编写的程序类型。这意味着,也许让人意外的是,它必须善于编写即弃程序。
即弃程序是一种你快速编写来完成某些有限任务的程序:自动化某些系统管理任务的程序,或为模拟生成测试数据的程序,或用于将数据从一种格式转换为另一种格式的程序。关于即弃程序的惊人之处在于,就像许多美国大学在二战期间建造的"临时"建筑一样,它们往往并不会被弃用。它们经常会演化成真正的程序,拥有真正的功能和真正的用户。
我猜想,最好的大型程序都是以这种方式开始的,而不是从一开始就像建霍夫大坝那样被设计成大型程序。从头开始构建一些很大的东西是一件可怕的事情。当人们承担一个太大的项目时,他们就会不知所措。这个项目要么陷入困境,要么结果会是生涩和笨拙的:一个购物中心而不是一个真正的市中心,一个巴西利亚而不是一个罗马,一个 Ada 而不是一个 C。
获得一个大型程序的另一种方式是从一个即弃程序开始,并不断改进它。这种方法不那么令人生畏,而且程序的设计也能从演化中获益。我认为,如果我们观察一下,就会发现这种方式才是大多数大型程序的开发方式。而那些确实是以这种方式发展起来的程序,很可能仍然使用它们最初编写时的编程语言,因为很少有程序会被移植,除非出于政治原因。因此,矛盾的是,如果你想创造一种被用于大型系统的语言,你必须让它善于编写即弃程序,因为大型系统就是从这里开始的。
Perl 是这一思想的一个引人注目的例子。它不仅是为编写即弃程序而设计的,而且它本身也差不多就是一个即弃程序。Perl 最初只是一个用于生成报告的工具集合,只有当人们用它编写的即弃程序变得越来越大时,它才逐步演化成一种编程语言。直到 Perl 5 (如果那时的话),该语言才适合编写重要程序,但它早已大受欢迎。
什么让一种语言善于编写即弃程序呢?首先,它必须是随时可用的。一个即弃程序是你希望在一小时内编写完的东西。所以这种语言可能必须已经安装在你正在使用的计算机上。它不能是你需要先安装才能使用的东西。它必须就在那里。C 之所以在那里,是因为它随操作系统一起发布。Perl 之所以在那里,是因为它最初是一个系统管理员的工具,而你的系统管理员已经安装好了它。
可用性不仅仅意味着已经安装好。一种具有命令行界面的交互式语言,比一种需要单独编译和运行的语言更可用。一种流行的编程语言应该是交互式的,而且启动快速。
对于即弃程序,另一个你想要的特性是简洁。简洁对黑客来说永远是有吸引力的,在你希望在一小时内完成的程序中尤其如此。
6. 库函数
当然,简洁到极致就是程序已经为你准备好了,你只需要调用它。这让我们想到我认为将会是编程语言日益重要的一个特性:库函数。Perl 之所以获胜,是因为它有大量的字符串处理库。这类库函数对于即弃程序特别重要,因为即弃程序通常最初是为了转换或提取数据而编写的。很多 Perl 程序可能只是由几个库调用粘合在一起开始的。
我认为,在未来的五十年里,编程语言的进步很大一部分将与库函数有关。我认为未来的编程语言将拥有和核心语言一样精心设计的库。编程语言设计不再是关于是否要让你的语言强类型还是弱类型,面向对象还是函数式,或者其他什么,而是关于如何设计出色的库。那些喜欢思考如何设计类型系统的语言设计师可能会感到颤栗。这几乎像在编写应用程序!太可惜了。语言是为程序员准备的,而库才是程序员最需要的。
设计好的库是很难的。这不仅仅是编写大量代码那么简单。一旦库变得太大,有时找到你需要的函数比自己编写代码还要花时间。库需要使用一组小的、正交的操作符来设计,就像核心语言一样。程序员应该能够猜出哪个库调用能完成他需要的功能。
在常用Lisp中,由于缺乏强大的字符串库和良好的操作系统支持,很难编写一个真正的大型程序。这个问题一直困扰着Lisp的发展。语法问题也是一个争议的话题。前缀表示法虽然对普通程序员来说不太友好,但对于熟练的黑客来说应该不是太大问题。我们可以通过一些技巧来减少括号的使用,如利用缩进来表示代码结构。有时中缀语法对某些表达式来说更容易理解,比如数学表达式。引入一些有限的语法到Lisp中应该是可以接受的,只要这些语法能够顺利地转换成底层的S表达式。
至于效率问题,语言设计并不是决定性因素,关键在于有一个优秀的分析器来发现性能瓶颈。一个主动式的分析器,能主动向程序员反馈性能数据,会更有助于提高程序的实际运行速度。视觉和声音反馈都是不错的方式,可以让程序员更直观地了解程序的运行状况。
[1]
即使有可能编写一个会自动检测效率低下算法的分析器也并不让人意外。记忆访问模式的特定模式可能是糟糕算法的可靠标志。如果有一个小家伙在计算机里执行我们的程序,他可能会像一个联邦政府雇员那样长而悲伤地讲述他的工作。我常常有一种感觉,好像我在让处理器追逐许多野鹅,但我从未有过良好的方式来观察它在做什么。
许多Lisp现在编译成字节码,然后由解释器执行。这通常是为了使实现更容易移植,但它可能是一个有用的语言特性。将字节码正式列为语言的一部分,并允许程序员在瓶颈中使用内联字节码可能是一个好主意。那样这些优化也将是可移植的。
最终用户感知到的速度性质可能正在发生变化。随着基于服务器的应用程序的出现,越来越多的程序可能会变成I/O受限。值得努力使I/O更快。语言可以通过简单、快速的格式化输出函数以及缓存和持久对象等深层次的结构性变化来帮助完成这一目标。
用户关心响应时间。但是另一种效率将越来越重要:每个处理器支持的同时用户数量。未来许多有趣的应用程序都将是基于服务器的,每台服务器支持的用户数量是提供此类应用程序的公司关心的关键问题。对于提供基于服务器的应用程序的业务,这是一个关键因素。
多年来,效率在大多数最终用户应用程序中并不重要。开发人员一直能够假设每个用户都将拥有日益强大的处理器。根据帕金森定律,软件已经扩展到利用可用资源。随着基于服务器的应用程序,这种情况将会改变。在那个世界里,硬件和软件将被一起提供。对于提供基于服务器的应用程序的公司来说,他们每台服务器能支持多少用户对于底线来说会产生很大影响。
在某些应用程序中,处理器将是限制因素,执行速度将是最重要的优化目标。但通常内存将是限制因素;同时用户的数量将由每个用户所需的数据量决定。语言也可以在这里提供帮助。对线程的良好支持将使所有用户共享单个堆。持久对象和/或语言级的延迟加载支持也可能会有帮助。
9 时间
一种流行语言所需要的最后一个因素就是时间。没有人愿意编写可能消失的语言程序,就像许多编程语言一样。所以大多数黑客在考虑使用一种语言之前,都会等待它至少存在两年。
发明了新事物的人常常会惊讶地发现,需要时间才能让任何信息传达给人们。我的一个朋友很少在第一次有人要求他做某事时就去做。他知道人们有时会要求他们最终并不想要的东西。为了避免浪费时间,他会等到有人第三次或第四次要求他做某事;到那时,无论问他的人现在有多恼火,至少他们可能真的想要他们要求的东西。
大多数人已经学会对新事物采取类似的过滤方式。他们直到听到某事十次之后才开始注意。这是完全合理的:大多数热门新事物最终都被证明是浪费时间,并最终消失。通过推迟学习VRML,我避免了不得不学习它的麻烦。
所以任何发明新事物的人都必须期望自己的信息要重复多年才能被人们开始接受。据我所知,我们编写的是第一个基于Web服务器的应用程序,但要让人们明白它不需要下载,我们花了好多年时间。这并不是因为他们很笨。他们只是忽略了我们。
好消息是,简单的重复就可以解决这个问题。你所要做的就是不断讲述你的故事,最终人们就会开始听到。引起人们注意的不是你出现的时间,而是你仍然存在的时间。
语言,特别是编程语言,在被首次推出后通常需要一段时间才能获得动力,这也并非完全坏事。大多数技术在被首次推出后都会继续发展改进-这对新技术来说再好不过了。拥有少数早期采用者作为用户是件好事,他们很专业、要求很高,能迅速暴露出你的技术中残留的任何缺陷。当你只有少数用户时,你可以与他们密切接触。早期采用者也很宽容当你改进系统时带来的一些中断。
新技术有两种引入方式:有机增长法和大爆炸法。有机增长法体现在经典的靠自己双手创业的小车库创业公司。几个人在默默无闻中开发出新技术,以毫无营销的方式推出,最初只有几个(狂热的)用户。他们不断改进技术,与此同时他们的用户群也通过口碑不断扩大。不知不觉中他们就变大了。
另一种方式,大爆炸法,则体现在风险投资支持、大量营销的创业公司。他们仓促开发产品,以大张旗鼓的方式推出,希望立即(可能)拥有大量用户。
通常来说,那些靠自己双手创业的人羡慕大爆炸派的人。大爆炸派人士很有亲和力、很有信心,受到风险投资公司的尊重。他们可以负担得起最好的一切,围绕推出的公关活动还能让他们成名。而那些坐在车库里的有机增长派人士则感到贫穷和被忽视。但我认为他们常常错误地为自己感到难过。有机增长似乎往往能产生更好的技术和更丰厚的创始人,而不是大爆炸法。如果你观察当今的主导技术,你会发现它们大多是以有机增长的方式发展起来的。
这种模式不仅适用于公司,也适用于赞助研究。Multics和Common Lisp是大爆炸式项目,而Unix和MacLisp则是有机增长式项目。
10 重新设计
正如E.B.White所说,"最好的写作就是重写"。每个优秀的作家都知道这一点,这在软件开发中也同样适用。设计的最重要部分就是重新设计。编程语言尤其需要重新设计,但往往没有得到足够的重视。
要编写出优秀的软件,你必须同时在头脑中保持两种对立的想法。你需要年轻黑客对自己能力的天真信心,同时也需要老练者的怀疑态度。你必须能够用大脑的一半思考"这有什么难的?[1]"同时用另一半思考"这肯定行不通[2]"。
这里的诀窍是,这两种想法并没有真正的矛盾。你应该对解决问题的可能性保持乐观,但对目前的解决方案保持怀疑态度。
做出出色工作的人往往认为他们正在做的工作并不好。别人看到他们的成果时会惊叹不已,但创造者自己满怀忧虑。这种模式并非偶然:正是这种忧虑使这项工作变得出色。
如果你能保持希望和忧虑的平衡,它们就会像两条腿推动自行车前进一样推动项目前进。在创新引擎的第一个阶段,你将全力以赴地解决某个问题,因为你有信心能够解决它。在第二个阶段,你会在清晨的冷光下审视自己的成果,并清楚地看到它的所有缺陷。但只要你的批评精神没有压过你的希望,你就能看着你承认还不完美的系统,想"这还有什么难的?"从而继续这个循环。
保持这两种力量平衡是很棘手的。在年轻黑客中,乐观占主导地位。他们产出一些东西,深信它很棒,从不进行改进。在老练的黑客中,怀疑占主导地位,他们连野心勃勃的项目都不敢开始。
任何有助于保持重新设计循环的事情都是好的。散文可以反复修改直到你满意。但软件通常不会经过足够的重新设计。散文有读者,但软件有用户。如果一位作者重写了一篇文章,阅读旧版本的人不太可能抱怨他们的思路受到了破坏性的不兼容性。
用户是把双刃剑。他们可以帮助你改进你的语言,但也可能阻止你改进它。所以要慎重选择你的用户,缓慢增加他们的数量。拥有用户就像优化一样:明智的做法是推迟它。此外,作为一般规则,你在任何给定时间都可以改变的比你想象的更多。引入变更就像拽掉创可贴:疼痛几乎立刻就会成为一种回忆。
每个人都知道,由一个委员会设计语言是个坏主意。委员会会产生糟糕的设计。但我认为委员会最糟糕的危险在于,它会干扰重新设计。引入变更需要大量的工作,所以没人想费心去做。无论委员会决定什么,通常都会保持下去,即使大部分成员不喜欢。
即使只有两个人的委员会也会阻碍重新设计。这种情况特别常见在由两个不同人编写的软件件之间的接口。要改变接口,两个人都必须同意同时改变它。因此,接口往往完全不会改变,这是个问题,因为它们往往是任何系统中最臃肿的部分之一。
这里的一个解决方案可能是设计系统,使接口是水平而不是垂直的——使模块总是垂直堆叠的抽象层。那样接口就会倾向于由其中的一个所拥有。在两个级别中,较低的级别要么是用上一个级别编写的语言,这种情况下较低级别将拥有接口,要么它是一个从属者,这种情况下接口可以由上一个级别来确定。
11 Lisp
这一切意味着,一种新的Lisp语言仍有希望出现。任何能给黑客想要的东西的语言都有希望,包括Lisp。我认为,我们可能错误地认为黑客被Lisp的怪异性所吓退。这种令人安慰的幻觉可能阻碍了我们看清Lisp,或至少是Common Lisp,的真正问题,那就是它不适合黑客想要做的事情。黑客语言需要强大的库和可以被黑的东西。Common Lisp两个都没有。黑客语言应该简洁易懂。Common Lisp则不是。
好消息是,不是Lisp有问题,而是Common Lisp。如果我们能开发出一种真正的黑客语言Lisp,我相信黑客们会使用它。他们会使用任何能更好地完成工作的语言。我们所要做的就是确保这种新的Lisp能够比其他语言更好地完成某些重要工作。
历史给我们一些鼓舞。随着时间的推移,新的编程语言不断从Lisp那里吸收更多的特性。在语言已经足够接近Lisp之前,已经没有太多可复制的东西了。目前最热门的语言Python,就是一种缺乏宏的、采用中缀语法的水化版Lisp。一种新的Lisp将是这种趋势的自然延续。
我有时觉得,把它称为Python的改进版可能是一个不错的营销策略。这听起来比Lisp时髦得多。对很多人来说,Lisp是一种有很多括号的缓慢的人工智能语言。Fritz Kunze的正式传记刻意回避了提及"L"字。但我猜,我们不应该害怕把这种新的Lisp称为Lisp。Lisp在最优秀的黑客中仍然有很高的影誉——比如那些上过6.001并真正理解的人。而这些就是你需要赢得的用户。
在《如何成为一名黑客》中,埃里克·雷蒙德把Lisp描述为一种类似拉丁语或希腊语的语言——你应该把它作为一种智力练习来学习,尽管你可能并不会真的经常使用它:
学习Lisp会给你一种终极的启蒙体验;这种体验将使你成为更好的程序员,即使你实际上并不会大量使用Lisp本身。
如果我不知道Lisp,读到这里我会问一些问题。如果一种语言能使我成为更好的程序员,那意味着它应该是一种更适合编程的语言。而这就是埃里克所暗示的。
只要这个想法还在流传,我认为黑客们对一种新的Lisp语言也会保持足够的开放态度,即使它被称为Lisp。但这种Lisp必须是一种黑客语言,就像20世纪70年代的经典Lisp一样。它必须简洁、简单、可被黑客改变。而且它必须拥有强大的库,以满足当今黑客的需求。
关于图书馆,我认为应该能够击败像Perl和Python这样的编程语言。在未来几年,需要编写的新应用程序大多是基于服务器的应用程序。一种新的Lisp语言完全可以拥有与Perl一样优秀的字符串库,如果这种新的Lisp语言还有强大的基于服务器的应用程序库,那它将非常受欢迎。真正的黑客不会对一种新工具视而不见,因为这种新工具可以让他们用几个库调用就能解决难题。记住,黑客都很懒惰。
如果新的Lisp语言在核心层面支持基于服务器的应用程序,那将是一个更大的胜利。例如,对于拥有多个用户的程序,或者对于数据所有权的类型标签级别的支持。
基于服务器的应用程序也为我们解决了这种新Lisp将被用来hack什么的问题。让Lisp成为Unix的脚本语言会是一个不错的选择(很难让它变得更糟)。但我认为也有一些领域现有的语言会更容易被击败。我认为最好是采用Tcl的模式,将Lisp与一个完整的支持基于服务器的应用程序的系统打包在一起。Lisp非常适合基于服务器的应用程序。词法闭包提供了一种在用户界面只是一系列网页时获得子程序效果的方法。S表达式很好地映射到HTML,宏擅长生成它。我们需要更好的工具来编写基于服务器的应用程序,也需要一种新的Lisp,这两者结合起来会非常完美。
12 梦想语言
为了总结,让我们尝试描述一下黑客梦想中的语言。这种梦想语言美丽、简洁、简洁。它有一个快速启动的交互式顶层。你可以用很少的代码编写程序来解决常见问题。你编写的任何程序中几乎所有的代码都是专门为你的应用程序而编写的。其他的一切都已经为你完成了。
这种语言的语法简洁到一种过错。你永远不需要输入不必要的字符,甚至很少使用Shift键。
使用大型抽象,你可以很快地编写出程序的第一个版本。后来,当你想要优化时,有一个非常好的分析器可以告诉你应该把注意力集中在哪里。你可以让内部循环极快,甚至可以编写内联字节码(如果需要的话)。
有很多好的例子可以学习,这种语言直观到你可以从例子中学会如何使用它,只需几分钟就能掌握,不需要查太多手册。手册很薄,很少有警告和限定条件。
这种语言有一个小的核心,以及强大且高度正交的库,它们的设计和核心语言一样精心。所有的库都能很好地协同工作;语言中的一切都像一台精细相机中的零件一样吻合。没有什么过时的东西,也没有为了兼容性而保留的东西。所有库的源代码都可以随时获取。与操作系统以及用其他语言编写的应用程序进行交互很容易。
这种语言是分层构建的。更高级的抽象是以一种非常透明的方式建立在更低级的抽象之上的,如果需要,你可以获得这些更低级的抽象。
没有什么是隐藏的,除非绝对必要。这种语言只提供抽象作为为你节省工作的一种方式,而不是告诉你该做什么。事实上,这种语言鼓励你成为它设计的平等参与者。你可以改变它的一切,包括它的语法,你写的任何东西都尽可能与预定义的内容具有同等地位。
注释
[1] 与现代概念非常相似的宏是由Timothy Hart在1965年提出的,在Lisp 1.5发布后两年。最初缺少的是避免变量捕获和多重评估的方法;Hart的示例受到这两者的影响。
[2] 在《当空气袭击你的大脑》一书中,神经外科医生Frank Vertosick讲述了一段对话,其中他的总住院医生加里谈论了外科医生和内科医生("跳蚤")之间的区别:
加里和我点了一个大披萨,找了一个空的隔间坐下来。主任点燃了一支香烟。"看看那些该死的跳蚤,唠叨着他们一生只会见到一次的那些奇怪的疾病。这就是跳蚤的问题,他们只喜欢那些离奇的东西。他们讨厌他们的主业案例。这就是我们和该死的跳蚤之间的区别。你看,我们热爱腰椎间盘突出,但他们讨厌高血压..."
很难把腰椎间盘突出想象成美味的(除非字面意义上的)。但我想我明白他们的意思。我经常有一些很有趣的bug要追踪。一个不是程序员的人很难想象到在一个bug上会有乐趣。当然,最好是一切都能正常工作。但毋庸置疑,追查某些类型的bug确实有一种阴郁的满足感。