(这篇文章最初是作为一种新语言的商业计划书而写的。因此,它遗漏了(因为它视之为理所当然)一个好的编程语言最重要的特性:极其强大的抽象能力。)

(This article was written as a kind of business plan for a new language. So it is missing (because it takes for granted) the most important feature of a good programming language: very powerful abstractions.)

我的一位朋友曾告诉一位著名的操作系统专家,说他想设计一门真正优秀的编程语言。这位专家对他说,那是浪费时间,编程语言的流行与否并不取决于其优缺点,所以不管他的语言有多好,也不会有人用。至少,自己设计的那门语言就是这样的遭遇。

A friend of mine once told an eminent operating systems expert that he wanted to design a really good programming language. The expert told him that it would be a waste of time, that programming languages don't become popular or unpopular based on their merits, and so no matter how good his language was, no one would use it. At least, that was what had happened to the language he had designed.

那么,究竟是什么让一门语言流行起来?流行的语言配得上它们的流行吗?去定义一门优秀的编程语言值得吗?如果值得,你又会怎么做?

What does make a language popular? Do popular languages deserve their popularity? Is it worth trying to define a good programming language? How would you do it?

我认为,这些问题的答案可以通过观察黑客并了解他们的需求来找到。编程语言是黑客服务的。一门编程语言作为编程语言本身(而不是作为指称语义或编译器设计的练习)是否优秀,当且仅当黑客喜欢它。

I think the answers to these questions can be found by looking at hackers, and learning what they want. Programming languages are for hackers, and a programming language is good as a programming language (rather than, say, an exercise in denotational semantics or compiler design) if and only if hackers like it.

1 流行的机制

1 The Mechanics of Popularity

当然,大多数人确实不单单根据语言的优缺点来选择编程语言。大多数程序员都是被别人告知该用什么语言。然而,我认为这种外部因素对编程语言流行度的影响并没有人们有时想象的那么大。我认为更大的问题在于,黑客眼中优秀的编程语言,与大多数语言设计者眼中的并不一样。

It's true, certainly, that most people don't choose programming languages simply based on their merits. Most programmers are told what language to use by someone else. And yet I think the effect of such external factors on the popularity of programming languages is not as great as it's sometimes thought to be. I think a bigger problem is that a hacker's idea of a good programming language is not the same as most language designers'.

在这两者之间,黑客的意见才起决定性作用。编程语言不是定理。它们是工具,是为人类设计的,它们必须像鞋子适应人脚一样,去适应人类的优势和弱点。如果一双鞋穿上时夹脚,不管它作为一件雕塑多么优雅,它都是一双烂鞋。

Between the two, the hacker's opinion is the one that matters. Programming languages are not theorems. They're tools, designed for people, and they have to be designed to suit human strengths and weaknesses as much as shoes have to be designed for human feet. If a shoe pinches when you put it on, it's a bad shoe, however elegant it may be as a piece of sculpture.

也许大多数程序员分不清语言的好坏。但这与其他任何工具并无二致。这并不意味着尝试设计一门优秀的语言是浪费时间。顶尖黑客一看到好语言就能认出来,并且会去使用它。诚然,顶尖黑客只是极少数,但这极少数人写出了所有优秀的软件,他们的影响力如此之大,以至于其他程序员往往会跟着使用他们所使用的任何语言。事实上,这往往不仅是影响,而是命令:顶尖黑客往往正是那些作为老板或导师,告诉其他程序员该用什么语言的人。

It may be that the majority of programmers can't tell a good language from a bad one. But that's no different with any other tool. It doesn't mean that it's a waste of time to try designing a good language. Expert hackers can tell a good language when they see one, and they'll use it. Expert hackers are a tiny minority, admittedly, but that tiny minority write all the good software, and their influence is such that the rest of the programmers will tend to use whatever language they use. Often, indeed, it is not merely influence but command: often the expert hackers are the very people who, as their bosses or faculty advisors, tell the other programmers what language to use.

顶尖黑客的意见并不是决定编程语言相对流行度的唯一力量——遗留软件(Cobol)和炒作(Ada、Java)也起到了作用——但我认为从长远来看,它是最强大的力量。只要有了最初的临界质量和足够的时间,一门编程语言大概就会达到它应得的流行程度。而流行会进一步将好语言与坏语言区分开来,因为来自真实用户的反馈总是会带来改进。看看任何流行的语言在其生命周期中发生了多么巨大的变化。Perl 和 Fortran 是极端的例子,但即使是 Lisp 也改变了许多。例如,Lisp 1.5 并没有宏(macro);这是后来在 MIT 的黑客花了几年时间用 Lisp 编写实际程序之后才演变出来的。[1]

The opinion of expert hackers is not the only force that determines the relative popularity of programming languages — legacy software (Cobol) and hype (Ada, Java) also play a role — but I think it is the most powerful force over the long term. Given an initial critical mass and enough time, a programming language probably becomes about as popular as it deserves to be. And popularity further separates good languages from bad ones, because feedback from real live users always leads to improvements. Look at how much any popular language has changed during its life. Perl and Fortran are extreme cases, but even Lisp has changed a lot. Lisp 1.5 didn't have macros, for example; these evolved later, after hackers at MIT had spent a couple years using Lisp to write real programs. [1]

所以,无论一门语言是否必须优秀才能流行,我认为一门语言必须流行才能变得优秀。而且它必须保持流行才能保持优秀。编程语言的技术水平不会停滞不前。然而,我们今天拥有的 Lisp 基本上仍是 20 世纪 80 年代中期 MIT 所拥有的样子,因为那是 Lisp 最后一次拥有足够庞大且挑剔的用户群体。

So whether or not a language has to be good to be popular, I think a language has to be popular to be good. And it has to stay popular to stay good. The state of the art in programming languages doesn't stand still. And yet the Lisps we have today are still pretty much what they had at MIT in the mid-1980s, because that's the last time Lisp had a sufficiently large and demanding user base.

当然,黑客必须先知道一门语言,然后才能使用它。他们要怎么听说呢?通过其他黑客。但是,必须有最初的一批黑客在使用这门语言,其他人才能听说它。我想知道这批人需要有多大;多少用户才能构成临界质量?凭直觉,我会说是 20 个人。如果一门语言有 20 个独立的用户,也就是 20 个自己决定使用它的用户,我就会认为它是真实存在的了。

Of course, hackers have to know about a language before they can use it. How are they to hear? From other hackers. But there has to be some initial group of hackers using the language for others even to hear about it. I wonder how large this group has to be; how many users make a critical mass? Off the top of my head, I'd say twenty. If a language had twenty separate users, meaning twenty users who decided on their own to use it, I'd consider it to be real.

达到这个目标绝非易事。如果从 0 到 20 比从 20 到 1000 还要难,我一点也不会感到惊讶。获取最初 20 个用户的最佳方法可能是使用特洛伊木马:给人们一个他们想要的应用程序,而这个程序恰好是用这门新语言编写的。

Getting there can't be easy. I would not be surprised if it is harder to get from zero to twenty than from twenty to a thousand. The best way to get those initial twenty users is probably to use a trojan horse: to give people an application they want, which happens to be written in the new language.

2 外部因素

2 External Factors

让我们首先承认一个确实会影响编程语言流行度的外部因素。要流行起来,一门编程语言必须是某个流行系统的脚本语言。Fortran 和 Cobol 是早期 IBM 大型机的脚本语言。C 是 Unix 的脚本语言,后来 Perl 也是。Tcl 是 Tk 的脚本语言。Java 和 Javascript 则是为了成为网页浏览器的脚本语言。

Let's start by acknowledging one external factor that does affect the popularity of a programming language. To become popular, a programming language has to be the scripting language of a popular system. Fortran and Cobol were the scripting languages of early IBM mainframes. C was the scripting language of Unix, and so, later, was Perl. Tcl is the scripting language of Tk. Java and Javascript are intended to be the scripting languages of web browsers.

Lisp 没能成为大众流行的语言,是因为它不是某个大众流行系统的脚本语言。它所保留的流行度可以追溯到 20 世纪 60 和 70 年代,当时它是 MIT 的脚本语言。那个时代许多伟大的程序员都曾与 MIT 有过交集。而在 70 年代初期 C 语言出现之前,MIT 的 Lisp 方言 MacLisp 是严肃黑客唯一想用的编程语言之一。

Lisp is not a massively popular language because it is not the scripting language of a massively popular system. What popularity it retains dates back to the 1960s and 1970s, when it was the scripting language of MIT. A lot of the great programmers of the day were associated with MIT at some point. And in the early 1970s, before C, MIT's dialect of Lisp, called MacLisp, was one of the only programming languages a serious hacker would want to use.

今天,Lisp 是两个中等流行系统(Emacs 和 Autocad)的脚本语言,因此我怀疑今天进行的大多数 Lisp 编程都是用 Emacs Lisp 或 AutoLisp 完成的。

Today Lisp is the scripting language of two moderately popular systems, Emacs and Autocad, and for that reason I suspect that most of the Lisp programming done today is done in Emacs Lisp or AutoLisp.

编程语言不会孤立存在。Hack(折腾、编写)是一个及物动词——黑客通常是在折腾某些东西——在实践中,语言是相对于它们用来折腾的东西来被评估的。因此,如果你想设计一门流行的语言,你要么必须提供比语言更多的东西,要么必须将你的语言设计成能替代某个现有系统的脚本语言。

Programming languages don't exist in isolation. To hack is a transitive verb — hackers are usually hacking something — and in practice languages are judged relative to whatever they're used to hack. So if you want to design a popular language, you either have to supply more than a language, or you have to design your language to replace the scripting language of some existing system.

Common Lisp 不流行,部分原因在于它是个“孤儿”。它最初确实带有一个可供折腾的系统:Lisp 机(Lisp Machine)。但在 20 世纪 80 年代,Lisp 机(连同并行计算机一起)被通用处理器日益强大的性能无情碾压。如果 Common Lisp 曾是 Unix 的优秀脚本语言,它或许还能保持流行。可惜,它在这方面糟糕透顶。

Common Lisp is unpopular partly because it's an orphan. It did originally come with a system to hack: the Lisp Machine. But Lisp Machines (along with parallel computers) were steamrollered by the increasing power of general purpose processors in the 1980s. Common Lisp might have remained popular if it had been a good scripting language for Unix. It is, alas, an atrociously bad one.

对这种情况的一种解释是,一门语言并非基于其自身的优缺点来被评判。另一种观点是,一门编程语言如果不是某种东西的脚本语言,它就根本不能算是一门真正的编程语言。这只有在出乎意料时才显得不公平。我认为这并不比期望一门编程语言应该有一个实现(implementation)更不公平。这本就是编程语言定义的一部分。

One way to describe this situation is to say that a language isn't judged on its own merits. Another view is that a programming language really isn't a programming language unless it's also the scripting language of something. This only seems unfair if it comes as a surprise. I think it's no more unfair than expecting a programming language to have, say, an implementation. It's just part of what a programming language is.

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

A programming language does need a good implementation, of course, and this must be free. Companies will pay for software, but individual hackers won't, and it's the hackers you need to attract.

一门语言还需要有一本关于它的书。这本书应该很薄、写得很好,并且充满优秀的例子。K&R(《C程序设计语言》)就是这方面的典范。目前,我几乎可以说一门语言必须有一本由 O'Reilly 出版的书。这正成为衡量它对黑客是否重要的标准。

A language also needs to have a book about it. The book should be thin, well-written, and full of good examples. K&R is the ideal here. At the moment I'd almost say that a language has to have a book published by O'Reilly. That's becoming the test of mattering to hackers.

还应该有在线文档。事实上,这本书可以先作为在线文档开始。但我不认为实体书已经过时。它们的排版很方便,而且出版社事实上的审查机制提供了一个有用(尽管不完美)的过滤器。书店是了解新语言最重要的地方之一。

There should be online documentation as well. In fact, the book can start as online documentation. But I don't think that physical books are outmoded yet. Their format is convenient, and the de facto censorship imposed by publishers is a useful if imperfect filter. Bookstores are one of the most important places for learning about new languages.

3 简洁

3 Brevity

假设你能提供任何语言都需要的这三样东西——免费的实现、一本书以及可供折腾的系统——你该如何做出一门黑客会喜欢的语言呢?

Given that you can supply the three things any language needs — a free implementation, a book, and something to hack — how do you make a language that hackers will like?

黑客喜欢的一点是简洁。黑客是懒惰的,就像数学家和现代主义建筑师的懒惰一样:他们讨厌任何多余的东西。如果说一个准备写程序的黑客在决定使用哪种语言时,至少在潜意识里是基于他必须输入的总字符数,这离事实并不遥远。如果这不完全是黑客的想法,语言设计者最好也当作这就是事实来行事。

One thing hackers like is brevity. Hackers are lazy, in the same way that mathematicians and modernist architects are lazy: they hate anything extraneous. It would not be far from the truth to say that a hacker about to write a program decides what language to use, at least subconsciously, based on the total number of characters he'll have to type. If this isn't precisely how hackers think, a language designer would do well to act as if it were.

试图用模仿英语的冗长表达来迎合用户是一个错误。Cobol 因这一缺陷而臭名昭著。黑客会认为,被要求写

It is a mistake to try to baby the user with long-winded expressions that are meant to resemble English. Cobol is notorious for this flaw. A hacker would consider being asked to write

add x to y giving z

add x to y giving z

而不是

instead of

z = x+y

z = x+y

这介于对智商的侮辱和对神灵的亵渎之间。

as something between an insult to his intelligence and a sin against God.

有时有人说,Lisp 应该用 firstrest 代替 carcdr,因为这会让程序更容易阅读。也许在最初的几个小时里是这样。但黑客可以很快学会 car 表示列表的第一个元素,cdr 表示其余部分。使用 firstrest 意味着要多打 50% 的字。而且它们的长度不同,这意味着当它们在连续的行中被调用时(carcdr 经常如此),参数无法对齐。我发现代码在页面上的对齐方式非常重要。当 Lisp 代码使用等宽字体以外的字体排版时,我几乎读不下去,朋友们说其他语言也是如此。

It has sometimes been said that Lisp should use first and rest instead of car and cdr, because it would make programs easier to read. Maybe for the first couple hours. But a hacker can learn quickly enough that car means the first element of a list and cdr means the rest. Using first and rest means 50% more typing. And they are also different lengths, meaning that the arguments won't line up when they're called, as car and cdr often are, in successive lines. I've found that it matters a lot how code lines up on the page. I can barely read Lisp code when it is set in a variable-width font, and friends say this is true for other languages too.

简洁是强类型语言失分的一个地方。在其他条件相同的情况下,没有人想在程序开头写一堆声明。任何可以隐式表达的东西,都应该隐式表达。

Brevity is one place where strongly typed languages lose. All other things being equal, no one wants to begin a program with a bunch of declarations. Anything that can be implicit, should be.

单个标记(token)也应该很短。Perl 和 Common Lisp 在这个问题上处于两个极端。Perl 程序可能密集得像密码,而 Common Lisp 内置操作符的名字则长得滑稽。Common Lisp 的设计者可能期望用户拥有能帮他们输入这些长名字的文本编辑器。但长名字的代价不仅仅是输入的代价,还有阅读的代价,以及它占用屏幕空间的代价。

The individual tokens should be short as well. Perl and Common Lisp occupy opposite poles on this question. Perl programs can be almost cryptically dense, while the names of built-in Common Lisp operators are comically long. The designers of Common Lisp probably expected users to have text editors that would type these long names for them. But the cost of a long name is not just the cost of typing it. There is also the cost of reading it, and the cost of the space it takes up on your screen.

4 可折腾性(Hackability)

4 Hackability

对黑客来说,有一件事比简洁更重要:能够做你想做的事。在编程语言的历史上,有惊人多的精力被用来阻止程序员做那些被认为是不妥当的事情。这是一个危险且自以为是的计划。语言设计者怎么可能知道程序员需要做什么?我认为语言设计者最好将他们的目标用户视为一个需要做他们从未预料到的事情的天才,而不是一个需要被保护免受自己伤害的笨蛋。笨蛋反正也会搬起石头砸自己的脚。你也许能阻止他引用另一个包中的变量,但你无法阻止他写出一个设计糟糕、解决错误问题且耗时无穷的程序。

There is one thing more important than brevity to a hacker: being able to do what you want. In the history of programming languages a surprising amount of effort has gone into preventing programmers from doing things considered to be improper. This is a dangerously presumptuous plan. How can the language designer know what the programmer is going to need to do? I think language designers would do better to consider their target user to be a genius who will need to do things they never anticipated, rather than a bumbler who needs to be protected from himself. The bumbler will shoot himself in the foot anyway. You may save him from referring to variables in another package, but you can't save him from writing a badly designed program to solve the wrong problem, and taking forever to do it.

优秀的程序员经常想做一些危险和“不体面”的事情。我所说的“不体面”,是指那些突破语言试图呈现的任何语义表象的事情:例如,获取某个高级抽象的内部表示。黑客喜欢折腾,而折腾意味着深入事物内部去挑战原设计者的想法。

Good programmers often want to do dangerous and unsavory things. By unsavory I mean things that go behind whatever semantic facade the language is trying to present: getting hold of the internal representation of some high-level abstraction, for example. Hackers like to hack, and hacking means getting inside things and second guessing the original designer.

让自己接受挑战。 当你制造任何工具时,人们都会以你未曾预料的方式使用它,对于像编程语言这样高度灵活的工具尤其如此。许多黑客会想以你从未想象过的方式调整你的语义模型。我说,让他们去吧;在不危及垃圾回收器等运行时系统的前提下,尽可能多地给程序员开放内部机制。

Let yourself be second guessed. When you make any tool, people use it in ways you didn't intend, and this is especially true of a highly articulated tool like a programming language. Many a hacker will want to tweak your semantic model in a way that you never imagined. I say, let them; give the programmer access to as much internal stuff as you can without endangering runtime systems like the garbage collector.

在 Common Lisp 中,我经常想遍历一个结构体(struct)的字段——例如,清理对已删除对象的引用,或者寻找未初始化的字段。我知道结构体底层其实就是向量(vector)。然而,我却无法写出一个可以对任何结构体调用的通用函数。我只能通过名字来访问字段,因为这才是结构体应该具有的含义。

In Common Lisp I have often wanted to iterate through the fields of a struct — to comb out references to a deleted object, for example, or find fields that are uninitialized. I know the structs are just vectors underneath. And yet I can't write a general purpose function that I can call on any struct. I can only access the fields by name, because that's what a struct is supposed to mean.

在一个大程序中,黑客可能只想颠覆一两次既定的事物模型。但是,能够做到这一点会带来多么巨大的差别。这可能不仅仅是解决问题的问题,这里面也有一种乐趣。黑客分享着外科医生在内脏中探索的隐秘乐趣,以及青少年挤青春痘的隐秘乐趣。[2] 至少对男孩子来说,某些恐怖的事物是迷人的。《Maxim》杂志每年都会出版一本画册,里面混杂了性感海报和惨烈的车祸照片。他们很了解自己的受众。

A hacker may only want to subvert the intended model of things once or twice in a big program. But what a difference it makes to be able to. And it may be more than a question of just solving a problem. There is a kind of pleasure here too. Hackers share the surgeon's secret pleasure in poking about in gross innards, the teenager's secret pleasure in popping zits. [2] For boys, at least, certain kinds of horrors are fascinating. Maxim magazine publishes an annual volume of photographs, containing a mix of pin-ups and grisly accidents. They know their audience.

从历史上看,Lisp 一直很擅长让黑客随心所欲。Common Lisp 的“政治正确”是一种反常。早期的 Lisp 让你能触碰到一切。幸运的是,这种精神很大程度上保留在宏中。能够对源代码进行任意的转换,这是一件多么美妙的事情。

Historically, Lisp has been good at letting hackers have their way. The political correctness of Common Lisp is an aberration. Early Lisps let you get your hands on everything. A good deal of that spirit is, fortunately, preserved in macros. What a wonderful thing, to be able to make arbitrary transformations on the source code.

经典的宏是真正的黑客工具——简单、强大且危险。理解它们的工作原理非常容易:你对宏的参数调用一个函数,它返回的任何内容都会被插入到宏调用的地方。卫生宏(hygienic macro)则体现了相反的原则。它们试图保护你,免得你理解它们在做什么。我从未听过有人能用一句话解释清楚卫生宏。它们是决定程序员被允许想要什么的典型危险例子。卫生宏旨在保护我免受变量捕捉(variable capture)的影响,但在某些宏中,变量捕捉恰恰是我想要的。

Classic macros are a real hacker's tool — simple, powerful, and dangerous. It's so easy to understand what they do: you call a function on the macro's arguments, and whatever it returns gets inserted in place of the macro call. Hygienic macros embody the opposite principle. They try to protect you from understanding what they're doing. I have never heard hygienic macros explained in one sentence. And they are a classic example of the dangers of deciding what programmers are allowed to want. Hygienic macros are intended to protect me from variable capture, among other things, but variable capture is exactly what I want in some macros.

一门真正优秀的语言应该既干净又肮脏:设计得干干净净,拥有一个由易于理解且高度正交的操作符组成的小型核心;但同时又是肮脏的,因为它允许黑客随心所欲地折腾它。C 语言就是这样。早期的 Lisp 也是如此。一门真正的黑客语言总是会带有一点放荡不羁的性格。

A really good language should be both clean and dirty: cleanly designed, with a small core of well understood and highly orthogonal operators, but dirty in the sense that it lets hackers have their way with it. C is like this. So were the early Lisps. A real hacker's language will always have a slightly raffish character.

一门优秀的编程语言应该具有一些让那些把“软件工程”挂在嘴边的人连连摇头、不以为然的特性。在光谱的另一端是像 Ada 和 Pascal 这样的语言,它们是规矩的典范,适合教学,仅此而已。

A good programming language should have features that make the kind of people who use the phrase "software engineering" shake their heads disapprovingly. At the other end of the continuum are languages like Ada and Pascal, models of propriety that are good for teaching and not much else.

5 一次性程序

5 Throwaway Programs

要对黑客产生吸引力,一门语言必须适合编写他们想写的程序。这也许令人惊讶,这意味着它必须适合编写一次性程序(throwaway programs)。

To be attractive to hackers, a language must be good for writing the kinds of programs they want to write. And that means, perhaps surprisingly, that it has to be good for writing throwaway programs.

一次性程序是指你为了某些有限的任务快速编写的程序:比如自动执行某些系统管理任务的程序,或者为模拟生成测试数据,或者将数据从一种格式转换为另一种格式。关于一次性程序,令人惊讶的是,就像二战期间许多美国大学建造的“临时”建筑一样,它们往往不会被扔掉。许多程序演变成了真正的程序,具有真正的功能和真实的用户。

A throwaway program is a program you write quickly for some limited task: a program to automate some system administration task, or generate test data for a simulation, or convert data from one format to another. The surprising thing about throwaway programs is that, like the "temporary" buildings built at so many American universities during World War II, they often don't get thrown away. Many evolve into real programs, with real features and real users.

我有一种直觉,最优秀的大型程序都是这样开始生命的,而不是像胡佛水坝那样从一开始就设计得很大。从头开始建造庞大的东西是令人畏惧的。当人们承担一个过大的项目时,他们会被压垮。项目要么陷入泥潭,要么结果是死板而生硬的:就像一个购物中心而不是一个真正的市中心,像巴西利亚而不是罗马,像 Ada 语言而不是 C 语言。

I have a hunch that the best big programs begin life this way, rather than being designed big from the start, like the Hoover Dam. It's terrifying to build something big from scratch. When people take on a project that's too big, they become overwhelmed. The project either gets bogged down, or the result is sterile and wooden: a shopping mall rather than a real downtown, Brasilia rather than Rome, Ada rather than C.

获得大型程序的另一种方法是从一次性程序开始,并不断改进它。这种方法不那么令人望而生畏,而且程序的设计能从演变中受益。我想,如果去调查一下,就会发现大多数大型程序都是这样开发出来的。而那些通过这种方式演变而来的程序,很可能仍然是用最初编写它们的语言写成的,因为除非出于政治原因,程序很少会被移植。因此,矛盾的是,如果你想创造一门用于大型系统的语言,你必须让它适合编写一次性程序,因为大型系统就是从那里来的。

Another way to get a big program is to start with a throwaway program and keep improving it. This approach is less daunting, and the design of the program benefits from evolution. I think, if one looked, that this would turn out to be the way most big programs were developed. And those that did evolve this way are probably still written in whatever language they were first written in, because it's rare for a program to be ported, except for political reasons. And so, paradoxically, if you want to make a language that is used for big systems, you have to make it good for writing throwaway programs, because that's where big systems come from.

Perl 是这一理念的醒目例子。它不仅是为编写一次性程序而设计的,而且它本身也几乎是一个一次性程序。Perl 的生命始于一系列用于生成报告的实用工具,只是随着人们用它编写的一次性程序变得越来越大,它才演变成了一门编程语言。直到 Perl 5(如果当时算的话),这门语言才适合编写严肃的程序,然而那时它已经极其流行了。

Perl is a striking example of this idea. It was not only designed for writing throwaway programs, but was pretty much a throwaway program itself. Perl began life as a collection of utilities for generating reports, and only evolved into a programming language as the throwaway programs people wrote in it grew larger. It was not until Perl 5 (if then) that the language was suitable for writing serious programs, and yet it was already massively popular.

是什么让一门语言适合写一次性程序?首先,它必须容易获取。一次性程序是你期望在一个小时内写完的东西。所以这门语言很可能必须已经安装在你正在使用的电脑上。它不能是你在使用前必须先安装的东西。它必须就在那里。C 语言在那里,因为它随操作系统一起提供。Perl 在那里,因为它最初是系统管理员的工具,而你的系统管理员已经安装了它。

What makes a language good for throwaway programs? To start with, it must be readily available. A throwaway program is something that you expect to write in an hour. So the language probably must already be installed on the computer you're using. It can't be something you have to install before you use it. It has to be there. C was there because it came with the operating system. Perl was there because it was originally a tool for system administrators, and yours had already installed it.

不过,容易获取不仅意味着已安装。一门带有命令行界面的交互式语言,比一门你必须单独编译和运行的语言更容易获取。一门流行的编程语言应该是交互式的,并且启动要快。

Being available means more than being installed, though. An interactive language, with a command-line interface, is more available than one that you have to compile and run separately. A popular programming language should be interactive, and start up fast.

在一次性程序中,你想要的另一件事是简洁。简洁对黑客来说总是很有吸引力,在他们期望一小时内搞定的程序中更是如此。

Another thing you want in a throwaway program is brevity. Brevity is always attractive to hackers, and never more so than in a program they expect to turn out in an hour.

6 库

6 Libraries

当然,极致的简洁是程序已经为你写好了,你只需要调用它。这就把我们带到了我认为在编程语言中将变得越来越重要的一个特性:库函数。Perl 胜出是因为它有用于操作字符串的庞大库。这类库函数对于一次性程序尤为重要,这些程序最初往往是为了转换或提取数据而编写的。许多 Perl 程序最初可能只是把几个库调用拼凑在一起。

Of course the ultimate in brevity is to have the program already written for you, and merely to call it. And this brings us to what I think will be an increasingly important feature of programming languages: library functions. Perl wins because it has large libraries for manipulating strings. This class of library functions are especially important for throwaway programs, which are often originally written for converting or extracting data. Many Perl programs probably begin as just a couple library calls stuck together.

我认为未来五十年编程语言取得的许多进步都将与库函数有关。我认为未来的编程语言将拥有与核心语言同样精心设计的库。编程语言设计将不再是关于让你的语言成为强类型还是弱类型、面向对象还是函数式等等,而是关于如何设计出伟大的库。那些喜欢思考如何设计类型系统的语言设计者可能会对此不寒而栗。这几乎就像是在写应用程序!太遗憾了。语言是为程序员服务的,而库正是程序员所需要的。

I think a lot of the advances that happen in programming languages in the next fifty years will have to do with library functions. I think future programming languages will have libraries that are as carefully designed as the core language. Programming language design will not be about whether to make your language strongly or weakly typed, or object oriented, or functional, or whatever, but about how to design great libraries. The kind of language designers who like to think about how to design type systems may shudder at this. It's almost like writing applications! Too bad. Languages are for programmers, and libraries are what programmers need.

设计好的库很难。这不仅仅是写很多代码的问题。一旦库变得太大,有时寻找所需函数的时间甚至比自己写代码的时间还要长。库需要使用一小组正交的操作符来设计,就像核心语言一样。程序员应该能够猜出什么样的库调用能实现他的需求。

It's hard to design good libraries. It's not simply a matter of writing a lot of code. Once the libraries get too big, it can sometimes take longer to find the function you need than to write the code yourself. Libraries need to be designed using a small set of orthogonal operators, just like the core language. It ought to be possible for the programmer to guess what library call will do what he needs.

库是 Common Lisp 欠缺的一个地方。它只有用于操作字符串的初级库,几乎没有用于与操作系统对话的库。由于历史原因,Common Lisp 试图假装操作系统不存在。而因为无法与操作系统对话,你很难仅仅使用 Common Lisp 的内置操作符来编写严肃的程序。你还必须使用一些特定于实现的技巧,而在实践中,这些往往无法给你想要的一切。如果 Common Lisp 拥有强大的字符串库和良好的操作系统支持,黑客对 Lisp 的评价会高得多。

Libraries are one place Common Lisp falls short. There are only rudimentary libraries for manipulating strings, and almost none for talking to the operating system. For historical reasons, Common Lisp tries to pretend that the OS doesn't exist. And because you can't talk to the OS, you're unlikely to be able to write a serious program using only the built-in operators in Common Lisp. You have to use some implementation-specific hacks as well, and in practice these tend not to give you everything you want. Hackers would think a lot more highly of Lisp if Common Lisp had powerful string libraries and good OS support.

7 语法

7 Syntax

一门具有 Lisp 语法(或者更准确地说,缺乏语法)的语言能够流行起来吗?我不知道这个问题的答案。我确实认为语法并不是 Lisp 目前不流行的主要原因。Common Lisp 有比陌生语法更严重的问题。我认识几个对前缀语法很适应的程序员,但他们默认使用 Perl,因为 Perl 拥有强大的字符串库并且可以与操作系统对话。

Could a language with Lisp's syntax, or more precisely, lack of syntax, ever become popular? I don't know the answer to this question. I do think that syntax is not the main reason Lisp isn't currently popular. Common Lisp has worse problems than unfamiliar syntax. I know several programmers who are comfortable with prefix syntax and yet use Perl by default, because it has powerful string libraries and can talk to the os.

前缀表示法有两个潜在的问题:程序员不熟悉,以及不够紧凑。Lisp 界的传统观点认为第一个问题是真正的症结所在。我不太确定。是的,前缀表示法会让普通程序员感到恐慌。但我认为普通程序员的意见并不重要。语言的流行与否取决于顶尖黑客对它们的看法,我认为顶尖黑客是能够应付前缀表示法的。Perl 的语法可能相当令人费解,但这并没有阻碍 Perl 的流行。如果说有什么作用的话,它可能还帮助培养了 Perl 的狂热崇拜。

There are two possible problems with prefix notation: that it is unfamiliar to programmers, and that it is not dense enough. The conventional wisdom in the Lisp world is that the first problem is the real one. I'm not so sure. Yes, prefix notation makes ordinary programmers panic. But I don't think ordinary programmers' opinions matter. Languages become popular or unpopular based on what expert hackers think of them, and I think expert hackers might be able to deal with prefix notation. Perl syntax can be pretty incomprehensible, but that has not stood in the way of Perl's popularity. If anything it may have helped foster a Perl cult.

一个更严重的问题是前缀表示法的冗长。对于顶尖黑客来说,这确实是个问题。当可以写出 a[x,y] 时,没有人想写 (aref a x y)

A more serious problem is the diffuseness of prefix notation. For expert hackers, that really is a problem. No one wants to write (aref a x y) when they could write a[x,y].

在这个特定的例子中,有一种巧妙避开问题的方法。如果我们把数据结构看作是索引上的函数,我们就可以写成 (a x y),这甚至比 Perl 的形式还要短。类似的技巧也可以缩短其他类型的表达式。

In this particular case there is a way to finesse our way out of the problem. If we treat data structures as if they were functions on indexes, we could write (a x y) instead, which is even shorter than the Perl form. Similar tricks may shorten other types of expressions.

我们可以通过使缩进具有语义来消除(或使之可选)大量的括号。反正程序员也是这样阅读代码的:当缩进表达一个意思而界定符表达另一个意思时,我们是以缩进为准的。将缩进视为具有语义将消除这一常见的 Bug 来源,同时使程序更短。

We can get rid of (or make optional) a lot of parentheses by making indentation significant. That's how programmers read code anyway: when indentation says one thing and delimiters say another, we go by the indentation. Treating indentation as significant would eliminate this common source of bugs as well as making programs shorter.

有时中缀语法更容易阅读。对于数学表达式尤其如此。我整个编程生涯都在使用 Lisp,但我仍然觉得前缀数学表达式不够自然。然而,特别是当你生成代码时,拥有可以接受任意数量参数的操作符是很方便的。所以如果我们确实要采用中缀语法,它大概应该作为某种读取宏(read-macro)来实现。

Sometimes infix syntax is easier to read. This is especially true for math expressions. I've used Lisp my whole programming life and I still don't find prefix math expressions natural. And yet it is convenient, especially when you're generating code, to have operators that take any number of arguments. So if we do have infix syntax, it should probably be implemented as some kind of read-macro.

我认为我们不应该在宗教信仰般地反对在 Lisp 中引入语法,只要它能以一种易于理解的方式翻译成底层的 S-表达式(s-expression)。Lisp 中已经有很多语法了。引入更多并不一定是坏事,只要不强迫任何人去使用它。在 Common Lisp 中,一些界定符是为语言保留的,这表明至少有一些设计者打算在未来引入更多的语法。

I don't think we should be religiously opposed to introducing syntax into Lisp, as long as it translates in a well-understood way into underlying s-expressions. There is already a good deal of syntax in Lisp. It's not necessarily bad to introduce more, as long as no one is forced to use it. In Common Lisp, some delimiters are reserved for the language, suggesting that at least some of the designers intended to have more syntax in the future.

Common Lisp 中最不符合 Lisp 风格的语法之一出现在格式化字符串(format strings)中;format 本身就是一门语言,而且那门语言不是 Lisp。如果有一个在 Lisp 中引入更多语法的计划,格式化说明符也许可以被包含在内。如果宏能像生成其他任何代码一样生成格式化说明符,那将是一件好事。

One of the most egregiously unlispy pieces of syntax in Common Lisp occurs in format strings; format is a language in its own right, and that language is not Lisp. If there were a plan for introducing more syntax into Lisp, format specifiers might be able to be included in it. It would be a good thing if macros could generate format specifiers the way they generate any other kind of code.

一位著名的 Lisp 黑客告诉我,他的那本《Common Lisp the Language》(CLTL)总是会自动翻开到 format 那一页。我的也是。这可能表明还有改进的空间。这也可能意味着程序做了大量的输入输出。

An eminent Lisp hacker told me that his copy of CLTL falls open to the section format. Mine too. This probably indicates room for improvement. It may also mean that programs do a lot of I/O.

8 效率

8 Efficiency

众所周知,一门优秀的语言应该生成快速的代码。但在实践中,我不认为快速的代码主要来自你在语言设计中所做的事情。正如高德纳(Knuth)很久以前指出的那样,速度只在某些关键的瓶颈中起作用。正如许多程序员此后所观察到的,人们常常在这些瓶颈所在的位置上犯错。

A good language, as everyone knows, should generate fast code. But in practice I don't think fast code comes primarily from things you do in the design of the language. As Knuth pointed out long ago, speed only matters in certain critical bottlenecks. And as many programmers have observed since, one is very often mistaken about where these bottlenecks are.

因此,在实践中,获得快速代码的方法是拥有一个非常好的性能分析器(profiler),而不是通过让语言成为强类型等手段。你不需要知道程序中每个调用中每个参数的类型。你确实需要在瓶颈中声明参数的类型。更重要的是,你需要能够找出瓶颈在哪里。

So, in practice, the way to get fast code is to have a very good profiler, rather than by, say, making the language strongly typed. You don't need to know the type of every argument in every call in the program. You do need to be able to declare the types of arguments in the bottlenecks. And even more, you need to be able to find out where the bottlenecks are.

人们对 Lisp 的一个抱怨是很难看出什么代价高昂。这可能是真的。如果你想拥有一门高度抽象的语言,这可能也是不可避免的。无论如何,我认为好的性能分析对解决这个问题大有帮助:你很快就能学会什么代价高昂。

One complaint people have had with Lisp is that it's hard to tell what's expensive. This might be true. It might also be inevitable, if you want to have a very abstract language. And in any case I think good profiling would go a long way toward fixing the problem: you'd soon learn what was expensive.

这里的部分问题是社会性的。语言设计者喜欢写快速的编译器。这就是他们衡量自己技能的方式。他们最多把性能分析器看作是一个附加组件。但在实践中,一个好的性能分析器在提高用该语言编写的实际程序的运行速度方面,可能比生成快速代码的编译器更有用。在这里,语言设计者又一次有些脱离了他们的用户。他们解决了一个稍微偏离了方向的问题,而且解决得非常好。

Part of the problem here is social. Language designers like to write fast compilers. That's how they measure their skill. They think of the profiler as an add-on, at best. But in practice a good profiler may do more to improve the speed of actual programs written in the language than a compiler that generates fast code. Here, again, language designers are somewhat out of touch with their users. They do a really good job of solving slightly the wrong problem.

拥有一个主动式性能分析器(active profiler)可能是个好主意——将性能数据推送给程序员,而不是等待他来询问。例如,当程序员编辑源代码时,编辑器可以用红色显示瓶颈。另一种方法是设法呈现运行中程序的情况。这在基于服务器的应用程序中将是一个特别大的胜利,因为在那儿你有大量的运行程序可以观察。主动式性能分析器可以图形化地显示程序运行时内存中发生的情况,甚至可以发出声音来告知发生的情况。

It might be a good idea to have an active profiler — to push performance data to the programmer instead of waiting for him to come asking for it. For example, the editor could display bottlenecks in red when the programmer edits the source code. Another approach would be to somehow represent what's happening in running programs. This would be an especially big win in server-based applications, where you have lots of running programs to look at. An active profiler could show graphically what's happening in memory as a program's running, or even make sounds that tell what's happening.

声音是发现问题的好线索。在我工作过的一个地方,我们有一个巨大的仪表盘,显示我们的 Web 服务器上正在发生的事情。指针是由微型伺服电机移动的,它们转动时会发出轻微的声音。我在办公桌前看不到仪表盘,但我发现,一旦服务器出现问题,我能立刻通过声音辨别出来。

Sound is a good cue to problems. In one place I worked, we had a big board of dials showing what was happening to our web servers. The hands were moved by little servomotors that made a slight noise when they turned. I couldn't see the board from my desk, but I found that I could tell immediately, by the sound, when there was a problem with a server.

甚至可能写出一个能自动检测低效算法的性能分析器。如果某些内存访问模式被证明是糟糕算法的确定迹象,我一点也不会感到惊讶。如果电脑内部有一个小人在运行我们的程序,他关于自己工作的抱怨,可能和联邦政府雇员一样漫长而哀怨。我经常有一种感觉,我让处理器做了很多无用功,但我从来没有一个好方法来观察它在做什么。

It might even be possible to write a profiler that would automatically detect inefficient algorithms. I would not be surprised if certain patterns of memory access turned out to be sure signs of bad algorithms. If there were a little guy running around inside the computer executing our programs, he would probably have as long and plaintive a tale to tell about his job as a federal government employee. I often have a feeling that I'm sending the processor on a lot of wild goose chases, but I've never had a good way to look at what it's doing.

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

A number of Lisps now compile into byte code, which is then executed by an interpreter. This is usually done to make the implementation easier to port, but it could be a useful language feature. It might be a good idea to make the byte code an official part of the language, and to allow programmers to use inline byte code in bottlenecks. Then such optimizations would be portable too.

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

The nature of speed, as perceived by the end-user, may be changing. With the rise of server-based applications, more and more programs may turn out to be i/o-bound. It will be worth making i/o fast. The language can help with straightforward measures like simple, fast, formatted output functions, and also with deep structural changes like caching and persistent objects.

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

Users are interested in response time. But another kind of efficiency will be increasingly important: the number of simultaneous users you can support per processor. Many of the interesting applications written in the near future will be server-based, and the number of users per server is the critical question for anyone hosting such applications. In the capital cost of a business offering a server-based application, this is the divisor.

多年来,在大多数终端用户应用程序中,效率并没有那么重要。开发人员可以假设每个用户的桌面上都会有一个日益强大的处理器。根据帕金森定律(Parkinson's Law),软件已经膨胀到可以消耗掉所有可用资源。随着基于服务器的应用程序的出现,这种情况将会改变。在那个世界里,硬件和软件将一起提供。对于提供基于服务器的应用程序的公司来说,他们每个服务器能支持多少用户,将对净利润产生非常大的影响。

For years, efficiency hasn't mattered much in most end-user applications. Developers have been able to assume that each user would have an increasingly powerful processor sitting on their desk. And by Parkinson's Law, software has expanded to use the resources available. That will change with server-based applications. In that world, the hardware and software will be supplied together. For companies that offer server-based applications, it will make a very big difference to the bottom line how many users they can support per server.

在某些应用程序中,处理器将是限制因素,执行速度将是最需要优化的东西。但内存往往才是限制所在;并发用户数将由你为每个用户的数据所需的内存量决定。语言在这里也可以提供帮助。对线程的良好支持将使所有用户能够共享一个堆。拥有持久化对象和/或语言级别的懒加载支持也可能会有帮助。

In some applications, the processor will be the limiting factor, and execution speed will be the most important thing to optimize. But often memory will be the limit; the number of simultaneous users will be determined by the amount of memory you need for each user's data. The language can help here too. Good support for threads will enable all the users to share a single heap. It may also help to have persistent objects and/or language level support for lazy loading.

9 时间

9 Time

流行语言需要的最后一种原料是时间。没有人想用一种可能会消失的语言来写程序,而许多编程语言确实消失了。所以大多数黑客往往会等一门语言出现几年后,才会考虑去使用它。

The last ingredient a popular language needs is time. No one wants to write programs in a language that might go away, as so many programming languages do. So most hackers will tend to wait until a language has been around for a couple years before even considering using it.

美妙新事物的发明者常常惊讶地发现这一点,但你需要时间来向人们传递任何信息。我的一个朋友很少在别人第一次要求他时做任何事。他知道人们有时会要求一些他们最终发现自己并不想要的东西。为了避免浪费时间,他会等到被要求第三次或第四次时才去做;到那时,提出请求的人可能会相当恼火,但至少他们可能真的想要他们所要求的东西。

Inventors of wonderful new things are often surprised to discover this, but you need time to get any message through to people. A friend of mine rarely does anything the first time someone asks him. He knows that people sometimes ask for things that they turn out not to want. To avoid wasting his time, he waits till the third or fourth time he's asked to do something; by then, whoever's asking him may be fairly annoyed, but at least they probably really do want whatever they're asking for.

大多数人已经学会了对他们听说的新事物进行类似的过滤。在听到十次之前,他们甚至不会开始关注。他们完全有理由这样做:大多数热门的新事物最终都被证明是浪费时间,并最终消失。通过推迟学习 VRML,我避免了去学习它。

Most people have learned to do a similar sort of filtering on new things they hear about. They don't even start paying attention until they've heard about something ten times. They're perfectly justified: the majority of hot new whatevers do turn out to be a waste of time, and eventually go away. By delaying learning VRML, I avoided having to learn it at all.

因此,任何发明新事物的人都必须准备好连续几年重复他们的信息,然后人们才会开始理解。我们编写了据我所知第一个基于 Web 服务器的应用程序,我们花了几年的时间才让人们明白它不需要下载。这并不是因为他们愚蠢,他们只是把我们屏蔽在外了。

So anyone who invents something new has to expect to keep repeating their message for years before people will start to get it. We wrote what was, as far as I know, the first web-server based application, and it took us years to get it through to people that it didn't have to be downloaded. It wasn't that they were stupid. They just had us tuned out.

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

The good news is, simple repetition solves the problem. All you have to do is keep telling your story, and eventually people will start to hear. It's not when people notice you're there that they pay attention; it's when they notice you're still there.

通常需要一段时间才能获得势头,这也是件好事。大多数技术即使在首次推出后也会发生很大的演变——尤其是编程语言。对于一项新技术来说,没有什么比前几年只被少数早期采用者使用更好的了。早期采用者精明且挑剔,会迅速清除你技术中残留的任何缺陷。当你只有少数用户时,你可以与他们所有人保持密切联系。而且当你改进系统时,即使这会造成一些破坏,早期采用者也是宽容的。

It's just as well that it usually takes a while to gain momentum. Most technologies evolve a good deal even after they're first launched — programming languages especially. Nothing could be better, for a new techology, than a few years of being used only by a small number of early adopters. Early adopters are sophisticated and demanding, and quickly flush out whatever flaws remain in your technology. When you only have a few users you can be in close contact with all of them. And early adopters are forgiving when you improve your system, even if this causes some breakage.

引入新技术有两种方式:有机增长法(organic growth method)和大爆炸法(big bang method)。有机增长法的典型代表是经典的、凭直觉行事、资金不足的车库创业公司。几个人在默默无闻中开发出了一些新技术。他们在没有营销的情况下推出它,最初只有几个(狂热奉献的)用户。他们继续改进技术,与此同时,他们的用户群通过口碑增长。在他们意识到之前,他们已经做大了。

There are two ways new technology gets introduced: the organic growth method, and the big bang method. The organic growth method is exemplified by the classic seat-of-the-pants underfunded garage startup. A couple guys, working in obscurity, develop some new technology. They launch it with no marketing and initially have only a few (fanatically devoted) users. They continue to improve the technology, and meanwhile their user base grows by word of mouth. Before they know it, they're big.

另一种方法,即大爆炸法,其典型代表是获得风投支持、进行大量营销的创业公司。他们急于开发出产品,在大肆宣传中推出,并立刻(他们希望)拥有庞大的用户群。

The other approach, the big bang method, is exemplified by the VC-backed, heavily marketed startup. They rush to develop a product, launch it with great publicity, and immediately (they hope) have a large user base.

通常,车库里的人会羡慕大爆炸的人。大爆炸的人圆滑、自信,受到风投的尊重。他们买得起一切最好的东西,围绕发布的公关活动顺便让他们成为了名人。呆在车库里的有机增长的人感到贫穷和不被爱。然而,我认为他们常常不该为自己感到难过。与大爆炸法相比,有机增长似乎能产生更好的技术和更富有的创始人。如果你看看当今占主导地位的技术,你会发现它们中的大多数都是有机增长起来的。

Generally, the garage guys envy the big bang guys. The big bang guys are smooth and confident and respected by the VCs. They can afford the best of everything, and the PR campaign surrounding the launch has the side effect of making them celebrities. The organic growth guys, sitting in their garage, feel poor and unloved. And yet I think they are often mistaken to feel sorry for themselves. Organic growth seems to yield better technology and richer founders than the big bang method. If you look at the dominant technologies today, you'll find that most of them grew organically.

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

This pattern doesn't only apply to companies. You see it in sponsored research too. Multics and Common Lisp were big-bang projects, and Unix and MacLisp were organic growth projects.

10 重新设计

10 Redesign

“最好的写作是重写,”埃尔温·布鲁克斯·怀特(E. B. White)写道。每个优秀的作家都知道这一点,对于软件也是如此。设计中最重要的部分是重新设计。尤其是编程语言,重新设计得还不够。

"The best writing is rewriting," wrote E. B. White. Every good writer knows this, and it's true for software too. The most important part of design is redesign. Programming languages, especially, don't get redesigned enough.

要写出优秀的软件,你必须在脑子里同时保持两种相反的想法。你需要年轻黑客对自己能力的幼稚信念,同时又需要老手的怀疑态度。你必须能够用半个大脑思考着这能有多难?,同时用另一半大脑思考着这永远行不通

To write good software you must simultaneously keep two opposing ideas in your head. You need the young hacker's naive faith in his abilities, and at the same time the veteran's skepticism. You have to be able to think how hard can it be? with one half of your brain while thinking it will never work with the other.

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

The trick is to realize that there's no real contradiction here. You want to be optimistic and skeptical about two different things. You have to be optimistic about the possibility of solving the problem, but skeptical about the value of whatever solution you've got so far.

做出优秀工作的人常常觉得他们正在做的事情不够好。其他人看到他们所做的事情并充满赞叹,但创造者自己却充满忧虑。这种模式并非巧合:正是这种忧虑使得工作变得优秀。

People who do good work often think that whatever they're working on is no good. Others see what they've done and are full of wonder, but the creator is full of worry. This pattern is no coincidence: it is the worry that made the work good.

如果你能保持希望与忧虑的平衡,它们将推动项目前进,就像你的两条腿驱动自行车前进一样。在双周期创新引擎的第一阶段,你凭着自己能够解决问题的信心,疯狂地致力于某个问题。在第二阶段,你在清晨冰冷的晨光中审视你所做的事情,非常清楚地看到它所有的缺陷。但只要你的批判精神没有压倒你的希望,你就能看着你这个公认不完整的系统,并想:把剩下的路走完能有多难?从而继续这个循环。

If you can keep hope and worry balanced, they will drive a project forward the same way your two legs drive a bicycle forward. In the first phase of the two-cycle innovation engine, you work furiously on some problem, inspired by your confidence that you'll be able to solve it. In the second phase, you look at what you've done in the cold light of morning, and see all its flaws very clearly. But as long as your critical spirit doesn't outweigh your hope, you'll be able to look at your admittedly incomplete system, and think, how hard can it be to get the rest of the way?, thereby continuing the cycle.

保持这两种力量的平衡是很微妙的。在年轻黑客中,乐观主义占据主导地位。他们做出了一些东西,确信它很棒,然后就再也不去改进它。在老黑客中,怀疑主义占据主导地位,他们甚至不敢承担有野心的项目。

It's tricky to keep the two forces balanced. In young hackers, optimism predominates. They produce something, are convinced it's great, and never improve it. In old hackers, skepticism predominates, and they won't even dare to take on ambitious projects.

任何能让重新设计循环继续进行的事情都是好的。散文可以一遍又一遍地重写,直到你满意为止。但软件通常没有得到足够的重新设计。散文有读者,而软件有用户。如果一个作家重写了一篇文章,读过旧版本的人不太可能会抱怨他们的思路被新引入的不兼容性打断了。

Anything you can do to keep the redesign cycle going is good. Prose can be rewritten over and over until you're happy with it. But software, as a rule, doesn't get redesigned enough. Prose has readers, but software has users. If a writer rewrites an essay, people who read the old version are unlikely to complain that their thoughts have been broken by some newly introduced incompatibility.

用户是一把双刃剑。他们可以帮助你改进语言,但也可能阻碍你改进它。所以要仔细选择你的用户,并放慢增长他们数量的速度。拥有用户就像优化:明智的做法是推迟它。此外,作为一条普遍规律,在任何特定时间,你都可以比你想象的做出更多的改变。引入改变就像撕掉创可贴:痛觉在你感受到它的那一刻就几乎成了记忆。

Users are a double-edged sword. They can help you improve your language, but they can also deter you from improving it. So choose your users carefully, and be slow to grow their number. Having users is like optimization: the wise course is to delay it. Also, as a general rule, you can at any given time get away with changing more than you think. Introducing change is like pulling off a bandage: the pain is a memory almost as soon as you feel it.

大家都知道,由委员会设计一门语言不是个好主意。委员会产生糟糕的设计。但我认为委员会最坏的危险在于它们干扰了重新设计。引入改变是如此麻烦,以至于没有人想去费事。委员会决定的任何事情都倾向于保持原样,即使大多数成员都不喜欢它。

Everyone knows that it's not a good idea to have a language designed by a committee. Committees yield bad design. But I think the worst danger of committees is that they interfere with redesign. It is so much work to introduce changes that no one wants to bother. Whatever a committee decides tends to stay that way, even if most of the members don't like it.

即使是两人的委员会也会阻碍重新设计。这尤其发生在由两个不同的人编写的软件片段之间的接口上。要改变接口,双方必须同时同意改变它。因此接口往往根本不发生改变,这是一个问题,因为它们往往是任何系统中最临时凑合的部分之一。

Even a committee of two gets in the way of redesign. This happens particularly in the interfaces between pieces of software written by two different people. To change the interface both have to agree to change it at once. And so interfaces tend not to change at all, which is a problem because they tend to be one of the most ad hoc parts of any system.

这里的一种解决方案可能是这样设计系统:使接口是水平的而不是垂直的——这样模块始终是垂直堆叠的抽象层。那么接口往往会归属于其中一方。两层中较低的一层要么是编写较高一层的语言,在这种情况下较低层将拥有接口;要么它是一个奴隶,在这种情况下接口可以由较高层决定。

One solution here might be to design systems so that interfaces are horizontal instead of vertical — so that modules are always vertically stacked strata of abstraction. Then the interface will tend to be owned by one of them. The lower of two levels will either be a language in which the upper is written, in which case the lower level will own the interface, or it will be a slave, in which case the interface can be dictated by the upper level.

11 Lisp

11 Lisp

所有这些意味着,一种新的 Lisp 还是有希望的。任何给黑客他们想要的东西的语言都有希望,包括 Lisp。我认为我们可能犯了一个错误,以为黑客被 Lisp 的奇异性吓跑了。这种自欺欺人的安慰可能阻止了我们看到 Lisp(至少是 Common Lisp)的真正问题,那就是它在做黑客想做的事情时表现得很糟糕。黑客的语言需要强大的库和可供折腾的系统。Common Lisp 两者都没有。黑客的语言是简洁且可折腾的。Common Lisp 不是。

What all this implies is that there is hope for a new Lisp. There is hope for any language that gives hackers what they want, including Lisp. I think we may have made a mistake in thinking that hackers are turned off by Lisp's strangeness. This comforting illusion may have prevented us from seeing the real problem with Lisp, or at least Common Lisp, which is that it sucks for doing what hackers want to do. A hacker's language needs powerful libraries and something to hack. Common Lisp has neither. A hacker's language is terse and hackable. Common Lisp is not.

好消息是,糟糕的不是 Lisp,而是 Common Lisp。如果我们能开发出一种新的、真正属于黑客的 Lisp,我认为黑客会使用它。只要能把工作做好,他们会使用任何语言。我们所要做的就是确保这种新的 Lisp 在某些重要工作上比其他语言做得更好。

The good news is, it's not Lisp that sucks, but Common Lisp. If we can develop a new Lisp that is a real hacker's language, I think hackers will use it. They will use whatever language does the job. All we have to do is make sure this new Lisp does some important job better than other languages.

历史提供了一些鼓励。随着时间的推移,接连出现的新编程语言从 Lisp 中汲取了越来越多的特性。在你创造的语言变成 Lisp 之前,已经没有多少东西可以抄了。最近热门的语言 Python,就是一种去掉了宏、带有中缀语法的稀释版 Lisp。一种新的 Lisp 将是这一进展中自然而然的一步。

History offers some encouragement. Over time, successive new programming languages have taken more and more features from Lisp. There is no longer much left to copy before the language you've made is Lisp. The latest hot language, Python, is a watered-down Lisp with infix syntax and no macros. A new Lisp would be a natural step in this progression.

我有时认为,把它称为 Python 的改进版本会是一个很好的营销伎俩。这听起来比 Lisp 更时髦。对许多人来说,Lisp 是一种带有大量括号、运行缓慢的 AI 语言。Fritz Kunze 的官方传记小心翼翼地避免提及 L 这个词。但我猜我们不应该害怕把新的 Lisp 叫做 Lisp。Lisp 在最顶尖的黑客中仍然有很多潜在的尊重——例如那些修过 6.001 课程并理解了它的人。而那些正是你需要争取的用户。

I sometimes think that it would be a good marketing trick to call it an improved version of Python. That sounds hipper than Lisp. To many people, Lisp is a slow AI language with a lot of parentheses. Fritz Kunze's official biography carefully avoids mentioning the L-word. But my guess is that we shouldn't be afraid to call the new Lisp Lisp. Lisp still has a lot of latent respect among the very best hackers — the ones who took 6.001 and understood it, for example. And those are the users you need to win.

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

In "How to Become a Hacker," Eric Raymond describes Lisp as something like Latin or Greek — a language you should learn as an intellectual exercise, even though you won't actually use it:

Lisp 值得学习,因为当你最终领悟它时,你将获得深刻的启迪体验;这种体验将在你余下的日子里使你成为一名更好的程序员,即使你实际上并不怎么使用 Lisp 本身。

Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot.

如果我不懂 Lisp,读到这里会让我提出疑问。一门能让我成为更好的程序员的语言,如果它有任何意义的话,意味着一门更适合编程的语言。而这实际上正是埃里克所说的话的含义。

If I didn't know Lisp, reading this would set me asking questions. A language that would make me a better programmer, if it means anything at all, means a language that would be better for programming. And that is in fact the implication of what Eric is saying.

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

As long as that idea is still floating around, I think hackers will be receptive enough to a new Lisp, even if it is called Lisp. But this Lisp must be a hacker's language, like the classic Lisps of the 1970s. It must be terse, simple, and hackable. And it must have powerful libraries for doing what hackers want to do now.

在库的问题上,我认为有空间在 Perl 和 Python 擅长的领域击败它们。未来几年需要编写的许多新应用程序都将是基于服务器的应用程序。没有理由一种新的 Lisp 不能拥有和 Perl 一样好的字符串库,如果这种新的 Lisp 还拥有用于基于服务器的应用程序的强大库,它可能会非常流行。真正的黑客不会对一个能让他们通过几个库调用就能解决难题的新工具嗤之以鼻。记住,黑客是懒惰的。

In the matter of libraries I think there is room to beat languages like Perl and Python at their own game. A lot of the new applications that will need to be written in the coming years will be server-based applications. There's no reason a new Lisp shouldn't have string libraries as good as Perl, and if this new Lisp also had powerful libraries for server-based applications, it could be very popular. Real hackers won't turn up their noses at a new tool that will let them solve hard problems with a few library calls. Remember, hackers are lazy.

如果核心语言能支持基于服务器的应用程序,那将是一个更大的胜利。例如,显式支持多用户程序,或者在类型标签级别支持数据所有权。

It could be an even bigger win to have core language support for server-based applications. For example, explicit support for programs with multiple users, or data ownership at the level of type tags.

基于服务器的应用程序也给了我们这个新 Lisp 将用来折腾什么的答案。让 Lisp 更好地作为 Unix 的脚本语言并没有坏处。(很难让它变得更糟了。)但我认为有些领域现有的语言更容易被击败。我认为模仿 Tcl 的模式,将 Lisp 与一个用于支持基于服务器的应用程序的完整系统结合起来提供,可能会更好。Lisp 是基于服务器的应用程序的天然契合者。当 UI 仅仅是一系列网页时,词法闭包(lexical closure)提供了一种获得子例程效果的方法。S-表达式可以很好地映射到 HTML 上,而宏擅长生成它。编写基于服务器的应用程序需要更好的工具,也需要一种新的 Lisp,这两者结合在一起会工作得非常好。

Server-based applications also give us the answer to the question of what this new Lisp will be used to hack. It would not hurt to make Lisp better as a scripting language for Unix. (It would be hard to make it worse.) But I think there are areas where existing languages would be easier to beat. I think it might be better to follow the model of Tcl, and supply the Lisp together with a complete system for supporting server-based applications. Lisp is a natural fit for server-based applications. Lexical closures provide a way to get the effect of subroutines when the ui is just a series of web pages. S-expressions map nicely onto html, and macros are good at generating it. There need to be better tools for writing server-based applications, and there needs to be a new Lisp, and the two would work very well together.

12 梦想中的语言

12 The Dream Language

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

By way of summary, let's try describing the hacker's dream language. The dream language is beautiful, clean, and terse. It has an interactive toplevel that starts up fast. You can write programs to solve common problems with very little code. Nearly all the code in any program you write is code that's specific to your application. Everything else has been done for you.

该语言的语法简洁得无以复加。你永远不需要输入一个不必要的字符,甚至不需要怎么使用 Shift 键。

The syntax of the language is brief to a fault. You never have to type an unnecessary character, or even to use the shift key much.

利用强大的抽象,你可以非常快速地编写出程序的第一版。稍后,当你想要优化时,会有一个非常好的性能分析器告诉你应该把注意力集中在哪里。你可以让内层循环变得快如闪电,如果需要,甚至可以编写内联字节码。

Using big abstractions you can write the first version of a program very quickly. Later, when you want to optimize, there's a really good profiler that tells you where to focus your attention. You can make inner loops blindingly fast, even writing inline byte code if you need to.

有许多好的例子可供学习,而且该语言足够直观,你可以在几分钟内通过例子学会如何使用它。你不需要怎么看手册。手册很薄,几乎没有警告和限制条件。

There are lots of good examples to learn from, and the language is intuitive enough that you can learn how to use it from examples in a couple minutes. You don't need to look in the manual much. The manual is thin, and has few warnings and qualifications.

该语言有一个小型的核心,以及与核心语言同样精心设计的、强大的、高度正交的库。这些库都能很好地协同工作;语言中的一切都像精美相机中的零件一样契合在一起。没有任何东西是被废弃的,或者为了兼容性而保留。所有库的源代码都是随时可用的。与操作系统以及用其他语言编写的应用程序对话非常容易。

The language has a small core, and powerful, highly orthogonal libraries that are as carefully designed as the core language. The libraries all work well together; everything in the language fits together like the parts in a fine camera. Nothing is deprecated, or retained for compatibility. The source code of all the libraries is readily available. It's easy to talk to the operating system and to applications written in other languages.

该语言是分层构建的。高级抽象是以非常透明的方式构建在低级抽象之上的,如果你想要,你可以获取这些低级抽象。

The language is built in layers. The higher-level abstractions are built in a very transparent way out of lower-level abstractions, which you can get hold of if you want.

除了绝对必须隐藏的东西之外,没有任何东西是对你隐藏的。该语言提供抽象只是为了替你省事,而不是为了指手画脚告诉你该做什么。事实上,该语言鼓励你成为其设计的平等参与者。你可以改变关于它的一切,甚至包括它的语法,而且你写的任何东西都尽可能地与预定义的东西具有同等的地位。

Nothing is hidden from you that doesn't absolutely have to be. The language offers abstractions only as a way of saving you work, rather than as a way of telling you what to do. In fact, the language encourages you to be an equal participant in its design. You can change everything about it, including even its syntax, and anything you write has, as much as possible, the same status as what comes predefined.

注释

Notes

[1] 1964 年,即 Lisp 1.5 发布两年后,蒂莫西·哈特(Timothy Hart)提出了与现代想法非常接近的宏。最初缺少的是避免变量捕捉和多次求值的方法;哈特的例子两者都存在。

[1] Macros very close to the modern idea were proposed by Timothy Hart in 1964, two years after Lisp 1.5 was released. What was missing, initially, were ways to avoid variable capture and multiple evaluation; Hart's examples are subject to both.

[2] 在《当空气碰到你的脑子》(When the Air Hits Your Brain)一书中,神经外科医生弗兰克·弗托西克(Frank Vertosick)讲述了一段对话,其中他的总住院医师加里(Gary)谈到了外科医生和内科医生(“跳蚤”)之间的区别:

[2] In When the Air Hits Your Brain, neurosurgeon Frank Vertosick recounts a conversation in which his chief resident, Gary, talks about the difference between surgeons and internists ("fleas"):

加里和我点了一个大比萨,找了一个空座位。老大点燃了一支烟。“看看那些该死的跳蚤,喋喋不休地谈论着他们一生中可能只会见一次的疾病。这就是跳蚤的毛病,他们只喜欢离奇的东西。他们讨厌最基本的病例。这就是我们和那些该死的跳蚤的区别。瞧,我们喜欢又大又多汁的腰椎间盘突出,但他们讨厌高血压……”

Gary and I ordered a large pizza and found an open booth. The chief lit a cigarette. "Look at those goddamn fleas, jabbering about some disease they'll see once in their lifetimes. That's the trouble with fleas, they only like the bizarre stuff. They hate their bread and butter cases. That's the difference between us and the fucking fleas. See, we love big juicy lumbar disc herniations, but they hate hypertension...."

很难把腰椎间盘突出想象成多汁的(除非字面意思)。然而我想我知道他们是什么意思。我经常有一个“多汁的”Bug 要去追踪。一个不是程序员的人很难想象 Bug 中会有乐趣。当然,如果一切都正常工作那就更好了。在某种程度上,确实如此。然而,在猎捕某些特定类型的 Bug 时,无可否认存在着一种冷酷的满足感。

It's hard to think of a lumbar disc herniation as juicy (except literally). And yet I think I know what they mean. I've often had a juicy bug to track down. Someone who's not a programmer would find it hard to imagine that there could be pleasure in a bug. Surely it's better if everything just works. In one way, it is. And yet there is undeniably a grim satisfaction in hunting down certain sorts of bugs.