Loading...

受欢迎

Original

2001 年 5 月

(本文是作为一种新语言的商业计划而撰写的。因此,它缺少(因为它认为是理所当然的)优秀编程语言的最重要的特性:非常强大的抽象。)

我的一个朋友曾经告诉一位著名的操作系统专家,他想设计一种真正优秀的编程语言。这位专家告诉他,这将是浪费时间,编程语言不会根据其优点而变得流行或不流行,所以无论他的语言有多好,都不会有人使用它。至少,这就是设计的语言所发生的事情。

什么使得一种语言流行?流行语言是否值得如此流行?定义一种好的编程语言是否值得?你会怎么做?

我认为这些问题的答案可以通过观察黑客并了解他们想要什么来找到。编程语言是黑客服务的,只有当黑客喜欢它时,编程语言才是一种好的编程语言(而不是指称语义或编译器设计的练习)。

1 人气机制

当然,大多数人不会仅仅根据编程语言的优点来选择编程语言。大多数程序员都是由别人来决定使用哪种语言。但我认为,这些外部因素对编程语言流行度的影响并不像人们有时认为的那么大。我认为更大的问题是,黑客对好编程语言的看法与大多数语言设计者的看法并不相同。

在这两者之间,黑客的意见才是最重要的。编程语言不是定理。它们是为人设计的工具,必须根据人的优点和缺点进行设计,就像鞋子必须根据人的脚来设计一样。如果鞋子穿起来很紧,那么这双鞋就是坏的,不管它是一件多么精美的雕塑。

可能大多数程序员都分不清好语言和坏语言。但使用其他工具也一样。这并不意味着尝试设计一种好的语言是浪费时间。专业黑客一看到好语言就能分辨出来,并且会使用它。诚然,专业黑客是极少数,但所有好的软件都是由这极少数人编写的,他们的影响力如此之大,以至于其他程序员都会倾向于使用他们使用的任何语言。事实上,通常这不仅仅是影响,而且是命令:通常专业黑客就是那些作为老板或教职顾问告诉其他程序员使用什么语言的人。

专业黑客的意见并不是决定编程语言流行程度的唯一因素——遗留软件(Cobol)和炒作(Ada、Java)也发挥了一定作用——但我认为从长远来看,这是最强大的因素。如果最初达到临界点并有足够的时间,一种编程语言可能就会变得像它应有的那么流行。而且流行程度进一步将好语言与坏语言区分开来,因为来自真实用户的反馈总能带来改进。看看任何一种流行语言在其生命周期中发生了多大的变化。Perl 和 Fortran 是极端情况,但即使是 Lisp 也发生了很大变化。例如,Lisp 1.5 没有宏;这些是后来发展起来的,在麻省理工学院的黑客们花了几年时间用 Lisp 编写真正的程序之后。[1]

所以,无论一种语言是否必须优秀才能流行,我认为一种语言必须流行才能优秀。而且它必须保持流行才能保持优秀。编程语言的最新发展不会停滞不前。然而,我们今天拥有的 Lisp 仍然与 20 世纪 80 年代中期麻省理工学院的 Lisp 基本相同,因为那是 Lisp 最后一次拥有足够大且要求严格的用户群。

当然,黑客必须先了解一门语言,然后才能使用它。他们怎么才能听说呢?从其他黑客那里。但必须有一些最初的黑客群体使用该语言,其他人才能听说它。我想知道这个群体有多大;多少用户才能达到临界点?我首先想到的是二十个。如果一种语言有二十个独立的用户,也就是说二十个用户自己决定使用它,我会认为它是真实的。

实现这一目标绝非易事。如果从零到二十比从二十到一千更难,我一点都不会感到惊讶。获得最初二十个用户的最佳方法可能是使用特洛伊木马:给人们一个他们想要的应用程序,而这个应用程序恰好是用新语言编写的。

2 外部因素

首先,让我们来了解一个影响编程语言流行度的外部因素。编程语言要想流行,就必须成为流行系统的脚本语言。Fortran 和 Cobol 是早期 IBM 大型机的脚本语言。C 是 Unix 的脚本语言,后来是 Perl。Tcl 是 Tk 的脚本语言。Java 和 Javascript 旨在成为 Web 浏览器的脚本语言。

Lisp 并不是一种非常流行的语言,因为它不是一种非常流行的系统的脚本语言。它保留的流行度可以追溯到 20 世纪 60 年代和 70 年代,当时它是 MIT 的脚本语言。当时很多伟大的程序员都曾与 MIT 有过合作。在 20 世纪 70 年代早期,在 C 语言出现之前,MIT 的 Lisp 方言 MacLisp 是严肃黑客唯一愿意使用的编程语言之一。

如今,Lisp 是两个比较流行的系统 Emacs 和 Autocad 的脚本语言,因此我怀疑当今大多数 Lisp 编程都是用 Emacs Lisp 或 AutoLisp 完成的。

编程语言并不是孤立存在的。黑客是一个及物动词——黑客通常是指黑客攻击某些东西——在实践中,语言的评判标准是它们被用来攻击的东西。因此,如果你想设计一种流行的语言,你要么必须提供不止一种语言,要么必须设计你的语言来取代某些现有系统的脚本语言。

Common Lisp 不受欢迎,部分原因是它是个孤儿。它最初确实带有一个可供破解的系统:Lisp Machine。但是,Lisp Machine(以及并行计算机)在 20 世纪 80 年代被通用处理器日益强大的性能所碾压。如果 Common Lisp 是 Unix 的一个好脚本语言,它可能会一直很受欢迎。可惜,它却是一个极其糟糕的脚本语言。

描述这种情况的一种方式是说,语言的优劣并不是根据其本身的优点来判断的。另一种观点是,一种编程语言除非也是某种东西的脚本语言,否则它就不能算作一种真正的编程语言。只有在它出乎意料时,这种说法才显得不公平。我认为这并不比期望一种编程语言有一个实现更不公平。它只是编程语言的一部分。

当然,编程语言确实需要良好的实现,而且必须是免费的。公司会为软件付费,但个人黑客不会,你需要吸引的是黑客。

一门语言也需要一本关于它的书。这本书应该很薄,写得很好,并且充满了很好的例子。K&R 是这里的理想选择。目前我几乎可以说,一门语言必须有一本由 O'Reilly 出版的书。这正在成为对黑客的考验。

还应该有在线文档。事实上,这本书可以从在线文档开始。但我认为实体书还没有过时。它们的格式很方便,出版商实施的事实审查制度是一个有用的过滤器,尽管不完美。书店是学习新语言最重要的地方之一。

3 简洁

假设你可以提供任何语言都需要的三样东西——一个免费的实现、一本书和一些可以破解的东西——你如何创造一种黑客会喜欢的语言?

黑客喜欢简洁。黑客很懒,就像数学家和现代主义建筑师一样:他们讨厌任何无关紧要的东西。说一个即将编写程序的黑客会根据他必须输入的字符总数来决定使用哪种语言,至少是潜意识中的决定,这并不夸张。如果黑客的想法不是这样,那么语言设计者最好假装是这样。

试图用长篇大论的表达方式来哄骗用户是错误的,这些表达方式本意是模仿英语。Cobol 因这个缺陷而臭名昭著。黑客会考虑被要求写

将 x 添加到 y 得到 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 可破解性

对于黑客来说,有一件事比简洁更重要:能够做你想做的事。在编程语言的历史上,人们付出了惊人的努力来阻止程序员做被认为不合适的事情。这是一个危险的妄自尊大的计划。语言设计师怎么知道程序员需要做什么?我认为语言设计师最好把他们的目标用户看作是一个需要做他们从未预料到的事情的天才,而不是一个需要自我保护的笨蛋。笨蛋无论如何都会搬起石头砸自己的脚。你可以让他免于引用另一个包中的变量,但你无法让他免于编写一个设计糟糕的程序来解决错误的问题,并花很长时间去解决它。

优秀的程序员经常想做一些危险和令人讨厌的事情。所谓令人讨厌的事情,指的是语言试图呈现的任何语义表象背后的事情:例如,掌握一些高级抽象的内部表示。黑客喜欢入侵,而入侵意味着进入事物内部并质疑原始设计者。

*让自己受到质疑。*当你制作任何工具时,人们都会以你意想不到的方式使用它,对于像编程语言这样高度清晰的工具来说尤其如此。许多黑客会想以你从未想象过的方式调整你的语义模型。我说,让他们去做吧;让程序员访问尽可能多的内部内容,而不会危及垃圾收集器等运行时系统。

在 Common Lisp 中,我经常想遍历结构体的字段 — 例如,梳理对已删除对象的引用,或查找未初始化的字段。我知道结构体只是底层的向量。但我无法编写一个可以在任何结构体上调用的通用函数。我只能通过名称访问字段,因为这就是结构体的意思。

黑客可能只想在一个大型程序中颠覆事物的预期模型一两次。但能够做到这一点会带来多大的不同。而且这可能不仅仅是解决问题的问题。这里也存在一种乐趣。黑客分享外科医生在窥探粗糙内脏时的秘密乐趣,青少年在挤痘痘时的秘密乐趣。[2] 至少对男孩来说,某些恐怖的东西是迷人的。《Maxim》杂志每年出版一期摄影集,里面有各种海报女郎和可怕的事故。他们了解自己的读者。

从历史上看,Lisp 一直擅长让黑客得逞。Common Lisp 的政治正确性是一种反常现象。早期的 Lisp 让你可以掌控一切。幸运的是,这种精神很大程度上保留在宏中。能够对源代码进行任意转换真是太棒了。

经典宏是真正的黑客工具——简单、强大且危险。它们的作用非常容易理解:你用宏的参数调用一个函数,它返回的任何内容都会插入到宏调用的位置。卫生宏体现了相反的原则。它们试图阻止你理解它们在做什么。我从未听过用一句话解释卫生宏。它们是决定程序员可以做什么的危险的典型例子。卫生宏旨在保护我免受变量捕获等影响,但变量捕获正是我在某些宏中想要的。

真正优秀的语言应该既干净又肮脏:设计干净,核心部分由易于理解且高度正交的运算符组成,但肮脏的意思是它可以让黑客随心所欲地使用它。C 就是这样。早期的 Lisp 也是如此。真正的黑客语言总是会有点粗俗的性格。

好的编程语言应该具有让那些使用“软件工程”一词的人不以为然的特性。而另一个极端是 Ada 和 Pascal 等语言,它们是适合教学的典范,仅此而已。

5 个一次性程序

要吸引黑客,一种语言必须适合编写他们想要编写的程序。这意味着,也许令人惊讶的是,它必须适合编写一次性程序。

一次性程序是快速编写的程序,用于完成某些有限的任务:用于自动执行某些系统管理任务、生成模拟测试数据或将数据从一种格式转换为另一种格式的程序。一次性程序的惊人之处在于,就像二战期间许多美国大学建造的“临时”建筑一样,它们通常不会被丢弃。许多程序会发展成为真正的程序,具有真正的功能和真正的用户。

我有一种预感,最好的大型项目都是这样开始的,而不是像胡佛大坝那样从一开始就设计得很宏大。从头开始建造一个大项目是可怕的。当人们接手一个太大的项目时,他们会不知所措。项目要么陷入困境,要么结果枯燥无味:一个购物中心而不是真正的市中心,巴西利亚而不是罗马,艾达而不是 C。

获得大型程序的另一种方法是从一次性程序开始,然后不断改进它。这种方法不那么令人生畏,程序的设计也受益于进化。我认为,如果有人仔细观察,就会发现大多数大型程序都是这样开发的。而那些以这种方式进化的程序可能仍然是用最初编写的语言编写的,因为除非出于政治原因,程序很少被移植。因此,矛盾的是,如果你想开发一种用于大型系统的语言,你就必须让它适合编写一次性程序,因为大型系统就是从那里来的。

Perl 就是这种想法的一个典型例子。它不仅被设计用于编写一次性程序,而且它本身几乎就是一个一次性程序。Perl 最初是一组用于生成报告的实用程序,随着人们用它编写的一次性程序越来越多,它才演变成一种编程语言。直到 Perl 5(如果那时的话)该语言才适合编写严肃的程序,但它已经非常流行了。

什么使得一种语言适合于编写一次性程序?首先,它必须随时可用。一次性程序是你期望在一小时内编写的东西。因此,这种语言可能必须已经安装在你正在使用的计算机上。它不能是你在使用前必须安装的东西。它必须在那里。C 语言在那里是因为它随操作系统一起提供。Perl 在那里是因为它最初是系统管理员的工具,而你的电脑已经安装了它。

不过,可用性不仅仅意味着安装。具有命令行界面的交互式语言比必须单独编译和运行的语言更可用。流行的编程语言应该是交互式的,并且启动速度快。

一次性程序的另一个优点是简洁。简洁对黑客来说总是有吸引力的,对于他们期望在一小时内完成的程序来说更是如此。

6 图书馆

当然,最简洁的方式是让程序已经为你写好,然后你只需调用它。而这又引出了我认为编程语言中越来越重要的一个特性:库函数。Perl 胜出是因为它拥有用于处理字符串的大型库。这类库函数对于一次性程序尤其重要,这些程序通常最初是为转换或提取数据而编写的。许多 Perl 程序可能一开始只是几个库调用拼凑在一起。

我认为,未来五十年编程语言的很多进步都将与库函数有关。我认为未来的编程语言将拥有与核心语言一样精心设计的库。编程语言设计将不再是要将语言设计成强类型还是弱类型、面向对象、函数式还是其他什么,而是如何设计出色的库。喜欢思考如何设计类型系统的语言设计者可能会对此感到不寒而栗。这几乎就像编写应用程序一样!太糟糕了。语言是为程序员设计的,而库是程序员所需要的。

设计好的库很难。这不仅仅是编写大量代码的问题。一旦库变得太大,有时找到所需的函数可能比自己编写代码还要花更长的时间。库需要使用一小组正交运算符来设计,就像核心语言一样。程序员应该能够猜测哪个库调用可以满足他的需求。

库是 Common Lisp 的不足之处。只有基本的字符串操作库,几乎没有与操作系统通信的库。由于历史原因,Common Lisp 试图假装操作系统不存在。而且由于您无法与操作系统通信,因此您不太可能仅使用 Common Lisp 中的内置运算符来编写严肃的程序。您还必须使用一些特定于实现的黑客技术,而实际上这些黑客技术往往不能满足您的所有需求。如果 Common Lisp 具有强大的字符串库和良好的操作系统支持,黑客们会对 Lisp 评价更高。

7 语法

一种具有 Lisp 语法的语言,或者更确切地说,缺乏语法的语言,会流行起来吗?我不知道这个问题的答案。我确实认为语法不是 Lisp 目前不流行的主要原因。Common Lisp 的问题比不熟悉的语法更严重。我认识几个程序员,他们熟悉前缀语法,但默认使用 Perl,因为它有强大的字符串库,可以与操作系统对话。

前缀表示法可能存在两个问题:程序员不熟悉它,以及它不够密集。Lisp 世界中的传统观点认为第一个问题才是真正的问题。我不太确定。是的,前缀表示法让普通程序员感到恐慌。但我认为普通程序员的意见并不重要。语言的流行与否取决于专业黑客对它们的看法,我认为专业黑客可能能够处理前缀表示法。Perl 语法可能非常难以理解,但这并没有妨碍 Perl 的流行。如果说有什么不同的话,那就是它可能有助于培养 Perl 狂热。

更严重的问题是前缀表示法的分散性。对于专业黑客来说,这确实是一个问题。当他们可以写 a[x,y] 时,没有人愿意写 (aref axy)。

在这个特殊情况下,有一种方法可以巧妙地解决这个问题。如果我们将数据结构视为索引上的函数,我们可以改写 (axy),这比 Perl 形式更短。类似的技巧可能会缩短其他类型的表达式。

通过使缩进变得有意义,我们可以去掉(或使之成为可选项)许多括号。无论如何,程序员都是这样阅读代码的:当缩进表示一件事而分隔符表示另一件事时,我们会以缩进为准。将缩进视为重要因素将消除这种常见的错误来源,并使程序更短。

有时中缀语法更容易阅读。对于数学表达式尤其如此。我整个编程生涯都在使用 Lisp,但我仍然觉得前缀数学表达式不自然。然而,使用可以接受任意数量参数的运算符很方便,尤其是在生成代码时。所以如果我们确实有中缀语法,它可能应该被实现为某种读取宏。

我认为我们不应该极力反对将语法引入 Lisp,只要它能够以一种易于理解的方式转换为底层的 s 表达式即可。Lisp 中已经有很多语法了。引入更多语法并不一定是坏事,只要没有人被迫使用它。在 Common Lisp 中,一些分隔符是为该语言保留的,这表明至少一些设计者打算在未来拥有更多语法。

Common Lisp 中最不合时宜的语法之一是格式字符串;格式本身就是一种语言,而这种语言不是 Lisp。如果有计划在 Lisp 中引入更多语法,格式说明符可能会包含在其中。如果宏可以像生成任何其他类型的代码一样生成格式说明符,那将是一件好事。

一位著名的 Lisp 黑客告诉我,他的 CLTL 副本对节格式不开放。我的也是。这可能表明还有改进的空间。这也可能意味着程序执行了大量 I/O。

8. 效率

众所周知,一门好的语言应该能生成快速的代码。但在实践中,我认为快速的代码主要不是来自于你在语言设计中所做的事情。正如 Knuth 很久以前指出的那样,速度只在某些关键瓶颈上才重要。而且正如许多程序员从那时起所观察到的,人们常常搞错这些瓶颈在哪里。

因此,在实践中,获得快速代码的方法是拥有一个非常好的分析器,而不是通过使语言强类型化。您不需要知道程序中每个调用的每个参数的类型。您确实需要能够声明瓶颈中的参数类型。甚至,您还需要能够找出瓶颈在哪里。

人们对 Lisp 的一个抱怨是,很难分辨哪些是昂贵的。这可能是真的。如果你想要一种非常抽象的语言,这也可能是不可避免的。无论如何,我认为良好的分析将大大有助于解决这个问题:你很快就会知道哪些是昂贵的。

这里的部分问题与社会有关。语言设计者喜欢编写快速的编译器。这就是他们衡量自己技能的方式。他们认为分析器充其量只是一个附加组件。但实际上,一个好的分析器可能比一个生成快速代码的编译器更能提高用该语言编写的实际程序的速度。在这里,语言设计者再次与他们的用户脱节。他们在解决稍微错误的问题方面做得很好。

拥有一个主动分析器可能是一个好主意——将性能数据推送给程序员,而不是等待他来询问。例如,当程序员编辑源代码时,编辑器可以用红色显示瓶颈。另一种方法是以某种方式表示正在运行的程序中发生的情况。这对于基于服务器的应用程序来说是一个特别大的好处,因为您需要查看许多正在运行的程序。主动分析器可以图形化地显示程序运行时内存中发生的情况,甚至可以发出声音来告诉您发生了什么。

声音是发现问题的良好线索。在我工作过的某个地方,我们有一块巨大的仪表盘,上面显示着我们的网络服务器正在发生什么。指针由小型伺服电机驱动,当它们转动时会发出轻微的声音。从我的办公桌上看不到仪表盘,但我发现,当服务器出现问题时,我可以通过声音立即判断出来。

甚至有可能编写一个可以自动检测低效算法的分析器。如果某些内存访问模式被证明是坏算法的确切迹象,我不会感到惊讶。如果计算机内部有一个小家伙在执行我们的程序,他可能会讲述一个关于他作为联邦政府雇员的工作的冗长而哀伤的故事。我经常有一种感觉,我让处理器做了很多徒劳无功的事情,但我从来没有一个很好的方法来观察它在做什么。

现在,许多 Lisp 都编译成字节码,然后由解释器执行。这样做通常是为了使实现更容易移植,但它可能是一个有用的语言特性。将字节码作为语言的正式组成部分,并允许程序员在瓶颈中使用内联字节码,这可能是一个好主意。这样,这种优化也将是可移植的。

最终用户对速度的感知可能正在发生变化。随着基于服务器的应用程序的兴起,越来越多的程序可能会受到 I/O 的限制。加快 I/O 速度是值得的。语言可以通过简单、快速、格式化的输出函数等直接措施提供帮助,也可以通过缓存和持久对象等深度结构变化提供帮助。

用户关心的是响应时间。但另一种效率将变得越来越重要:每个处理器可以同时支持的用户数量。在不久的将来,许多有趣的应用程序将基于服务器,而每个服务器的用户数量对于任何托管此类应用程序的人来说都是一个关键问题。在提供基于服务器的应用程序的企业的资本成本中,这是除数。

多年来,效率在大多数终端用户应用程序中并不重要。开发人员可以假设每个用户的办公桌上都会有一台功能越来越强大的处理器。根据帕金森定律,软件已经扩展到使用可用资源。这种情况将随着基于服务器的应用程序而改变。在那个世界里,硬件和软件将一起提供。对于提供基于服务器的应用程序的公司来说,每台服务器可以支持多少用户将对利润产生很大的影响。

在某些应用程序中,处理器将成为限制因素,而执行速度将是最重要的优化因素。但通常内存才是限制因素;同时使用的用户数量将由每个用户的数据所需的内存量决定。语言也可以提供帮助。对线程的良好支持将使所有用户能够共享单个堆。持久对象和/或语言级别对延迟加载的支持也可能有所帮助。

9时间

流行语言需要的最后一个要素是时间。没有人愿意用一种可能会消失的语言编写程序,而许多编程语言都是如此。因此,大多数黑客倾向于等到一种语言出现几年后才考虑使用它。

发明新奇事物的人常常会惊讶地发现这一点,但你需要时间才能将任何信息传达给人们。我的一个朋友很少在第一次有人问他时就采取行动。他知道人们有时会要求他们最终不想要的东西。为了避免浪费时间,他会等到第三次或第四次被要求做某事;到那时,无论谁问他,他可能都会相当恼火,但至少他们可能真的想要他们所要求的东西。

大多数人都学会了对听到的新事物进行类似的过滤。他们甚至直到听到某件事十次后才开始关注。这是完全有道理的:大多数热门的新事物最终都是浪费时间,最终会消失。通过推迟学习 VRML,我完全避免了学习它。

因此,任何发明新东西的人都必须做好多年不断重复其信息,人们才会开始理解的准备。据我所知,我们编写了第一个基于网络服务器的应用程序,我们花了数年时间才让人们明白它不需要下载。这并不是因为他们愚蠢。他们只是让我们不感兴趣。

好消息是,简单的重复就能解决问题。你所要做的就是继续讲述你的故事,最终人们会开始倾听。人们不是在注意到你在那里时才开始关注你,而是在他们注意到你仍然在那里时才开始关注你。

通常需要一段时间才能获得发展势头,这也是正常的。大多数技术在首次推出后都会得到很大的发展——编程语言尤其如此。对于一项新技术来说,没有什么比几年只被少数早期采用者使用更好的了。早期采用者经验丰富,要求严格,会迅速消除技术中存在的缺陷。当你只有少数用户时,你可以与所有人保持密切联系。当你改进系统时,早期采用者会很宽容,即使这会导致一些破坏。

新技术的引入有两种方式:有机增长法和大爆炸法。有机增长法的典型代表是典型的资金不足的车库创业公司。几个默默无闻的人开发了一些新技术。他们没有进行任何营销就推出了这项技术,最初只有少数(狂热的)用户。他们继续改进技术,与此同时,他们的用户群通过口口相传而增长。不知不觉中,他们就变得很强大了。

另一种方法,即大爆炸法,以风险投资支持、大力营销的初创公司为代表。他们急于开发产品,大力宣传,并立即(他们希望)拥有庞大的用户群。

一般来说,车库创业者羡慕大爆炸创业者。大爆炸创业者圆滑、自信,受到风险投资家的尊重。他们买得起最好的东西,而围绕产品发布的公关活动也让他们成为了名人。有机增长创业者坐在车库里,感觉自己很穷,没有人爱。但我认为他们常常误以为自己可怜自己。有机增长似乎比大爆炸方法能带来更好的技术和更富有的创始人。如果你看看今天的主流技术,你会发现它们中的大多数都是有机增长的。

这种模式不仅适用于公司。你也可以在赞助研究中看到它。Multics 和 Common Lisp 是大爆炸项目,而 Unix 和 MacLisp 是有机增长项目。

10 重新设计

“最好的写作就是重写,”EB White 写道。每个优秀的作家都知道这一点,对于软件来说也是如此。设计中最重要的部分是重新设计。尤其是编程语言,没有得到足够的重新设计。

要编写出好的软件,你必须同时在脑子里保持两种相反的想法。你需要年轻黑客对自己能力的天真信念,同时还要有老手的怀疑态度。你必须能够用一半大脑思考这有多难?而用另一半大脑认为这永远不会奏效

诀窍是要意识到这里并没有真正的矛盾。你要对两件不同的事情保持乐观和怀疑。你必须对解决问题的可能性保持乐观,但对目前得到的任何解决方案的价值保持怀疑。

做得好的人常常认为自己所做的一切都不好。别人看到他们所做的一切,会感到惊叹,但创作者却充满忧虑。这种模式并非巧合:正是忧虑让作品变得好。

如果你能保持希望和担忧的平衡,它们就会推动项目向前发展,就像你的两条腿推动自行车前进一样。在双循环创新引擎的第一阶段,你会疯狂地解决某个问题,因为你相信自己能够解决它。在第二阶段,你会在清晨的清晨审视自己所做的事情,并清楚地看到它的所有缺陷。但只要你的批判精神不超过你的希望,你就能审视你承认不完整的系统,并思考,完成剩下的工作有多难?从而继续这个循环。

保持这两股力量的平衡是件棘手的事。在年轻黑客中,乐观主义占主导地位。他们创造出一些东西,相信它很棒,但从不改进它。在老黑客中,怀疑主义占主导地位,他们甚至不敢承担雄心勃勃的项目。

任何可以保持重新设计周期的措施都是好的。散文可以反复重写,直到你满意为止。但软件通常不会得到足够的重新设计。散文有读者,但软件有*用户。*如果作者重写了一篇文章,阅读旧版本的人不太可能抱怨他们的思想被一些新引入的不兼容性所破坏。

用户是一把双刃剑。他们可以帮助你改进语言,但也可能阻碍你改进语言。因此,请谨慎选择用户,并慢慢增加用户数量。拥有用户就像优化:明智的做法是推迟它。此外,一般来说,你可以在任何时候进行比你想象的更多的更改。引入更改就像撕掉绷带:疼痛几乎在你感觉到时就变成了记忆。

每个人都知道,让委员会设计语言不是一个好主意。委员会的设计很糟糕。但我认为委员会最大的危险是它们会干扰重新设计。引入变化的工作量太大,没人愿意费心。委员会决定的一切往往都会保持不变,即使大多数成员不喜欢它。

即使是两个人组成的委员会也会妨碍重新设计。这种情况尤其发生在由两个不同的人编写的软件之间的界面上。要更改界面,两人必须同时同意更改。因此,界面往往根本不会改变,这是一个问题,因为它们往往是任何系统中最临时的部分之一。

这里的一个解决方案可能是设计系统,使接口水平而不是垂直 — — 这样模块总是垂直堆叠的抽象层。然后接口将倾向于由其中一个拥有。两个级别中的较低级别要么是编写较高级别的语言,在这种情况下较低级别将拥有接口,要么它将是从属的,在这种情况下接口可以由较高级别决定。

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 是一种带有大量括号的缓慢 AI 语言。Fritz Kunze 的官方传记小心翼翼地避免提及“Lisp”这个词。但我猜我们不应该害怕将新的 Lisp 称为 Lisp。Lisp 在最优秀的黑客中仍然享有很高的声誉——例如,那些学习了 6.001 并理解它的人。而这些正是你需要赢得的用户。

在《如何成为一名黑客》一书中,Eric Raymond 将 Lisp 描述为类似于拉丁语或希腊语的语言——一种你应该作为一种智力练习来学习的语言,即使你实际上不会使用它:

Lisp 值得学习,因为当你最终掌握它时,你将获得深刻的启迪;即使你实际上从未大量使用过 Lisp 本身,这种经验也将使你在余生中成为一名更好的程序员。

如果我不知道 Lisp,读到这篇文章我会开始问问题。如果说一种语言能让我成为更好的程序员,那么它就意味着一种更适合编程的语言。而这实际上就是 Eric 所说的含义。

只要这个想法还在流传,我认为黑客们就会对新的 Lisp 足够接受,即使它被称为 Lisp。但这种 Lisp 必须是一种黑客语言,就像 20 世纪 70 年代的经典 Lisp 一样。它必须简洁、简单且易于破解。而且它必须有强大的库来做黑客现在想做的事情。

在库方面,我认为在 Perl 和 Python 等语言的领域中,它们有胜算。未来几年需要编写的许多新应用程序将是基于服务器的应用程序。没有理由说新的 Lisp 不应该有与 Perl 一样好的字符串库,如果这个新的 Lisp 也有强大的基于服务器的应用程序库,它可能会非常受欢迎。真正的黑客不会对一个能让他们通过几个库调用解决难题的新工具嗤之以鼻。记住,黑客是懒惰的。

为基于服务器的应用程序提供核心语言支持可能会带来更大的好处。例如,明确支持具有多个用户的程序,或在类型标签级别上支持数据所有权。

基于服务器的应用程序也为我们解答了这个问题:这种新的 Lisp 将用于破解什么。将 Lisp 改进为 Unix 的脚本语言不会有什么坏处。(让它变得更糟就很难了。)但我认为有些领域现有的语言更容易被击败。我认为最好遵循 Tcl 的模型,并提供 Lisp 以及一个完整的系统来支持基于服务器的应用程序。Lisp 非常适合基于服务器的应用程序。当用户界面只是一系列网页时,词法闭包提供了一种获得子例程效果的方法。S 表达式可以很好地映射到 html 上,宏非常适合生成它。需要有更好的工具来编写基于服务器的应用程序,并且需要一种新的 Lisp,两者可以很好地协同工作。

12 梦的语言

总结一下,让我们试着描述一下黑客的梦想语言。梦想语言是优美、干净和简洁的。它有一个启动速度很快的交互式顶层。你可以用很少的代码编写程序来解决常见问题。你编写的任何程序中的几乎所有代码都是特定于你的应用程序的代码。其他一切都为你做好了。

该语言的语法非常简洁。您无需输入不必要的字符,甚至无需频繁使用 Shift 键。

使用大型抽象,您可以非常快速地编写程序的第一个版本。稍后,当您想要优化时,有一个非常好的分析器会告诉您应该关注哪里。您可以使内部循环变得非常快,甚至可以在需要时编写内联字节代码。

有很多很好的例子可以学习,而且语言非常直观,你可以在几分钟内从例子中学会如何使用它。你不需要经常查看手册。手册很薄,几乎没有警告和限定。

该语言的核心很小,但有功能强大、高度正交的库,这些库的设计与核心语言一样精心。所有库都可以很好地协同工作;该语言中的所有东西都像精密相机中的零件一样紧密贴合。没有任何东西被弃用或保留以保证兼容性。所有库的源代码都很容易获得。与操作系统和用其他语言编写的应用程序对话很容易。

该语言是分层构建的。高级抽象是通过低级抽象以非常透明的方式构建的,如果需要,您可以随时掌握这些抽象。

没有什么是绝对必要的,它不会对你隐藏。该语言提供抽象只是为了节省你的工作量,而不是告诉你该做什么。事实上,该语言鼓励你成为其设计的平等参与者。你可以改变它的一切,甚至包括它的语法,你写的任何内容都尽可能与预定义的内容具有相同的状态。

笔记

[1] 1964 年,即 Lisp 1.5 发布两年后,Timothy Hart 提出了与现代理念非常接近的宏。最初缺少的是避免变量捕获和多重求值的方法;Hart 的示例都受制于这两者。

[2] 在*《当空气冲击你的大脑》一书中,*神经外科医生弗兰克·维尔托西克记述了他的住院总医师加里与他谈论外科医生和内科医生(“跳蚤”)之间的区别时的一次谈话:

加里和我点了一份大披萨,找到了一个空着的摊位。老板点了一支烟。“看看这些该死的跳蚤,喋喋不休地说着它们一生中只会遇到一次的疾病。这就是跳蚤的麻烦,它们只喜欢稀奇古怪的东西。它们讨厌自己的面包和黄油壳。这就是我们和这些该死的跳蚤之间的区别。你看,我们喜欢腰椎间盘突出症,但它们讨厌高血压……”

很难想象腰椎间盘突出症是有趣的(除了字面意思)。但我想我知道他们的意思。我经常要追踪有趣的错误。非程序员很难想象错误会带来乐趣。当然,如果一切都正常,那就更好了。从某种意义上说,确实如此。然而,不可否认的是,追踪某些类型的错误会带来一种可怕的满足感。