Loading...

超越平均水平

Original

2001 年 4 月,2003 年 4 月修订

(本文源自 2001 年 Franz 开发者研讨会上的一次演讲。)

1995 年夏天,我和朋友罗伯特·莫里斯创办了一家名为Viaweb的创业公司。我们的计划是编写软件,让最终用户可以建立在线商店。当时,这款软件的新颖之处在于它在我们的服务器上运行,使用普通网页作为界面。

当然,很多人可能同时有这个想法,但据我所知,Viaweb 是第一个基于 Web 的应用程序。对我们来说,这个想法太新颖了,我们以它的名字命名了公司:Viaweb,因为我们的软件是通过 Web 运行的,而不是在台式计算机上运行。

这款软件的另一个不同寻常之处在于,它主要用一种名为 Lisp 的编程语言编写而成。它是第一批用 Lisp 编写的大型终端用户应用程序之一,在此之前,Lisp 主要用于大学和研究实验室。[1]

秘密武器

Eric Raymond 写过一篇名为《如何成为一名黑客》的文章,其中,他告诉了那些想成为黑客的人应该学习哪些语言。他建议从 Python 和 Java 开始,因为它们很容易学。认真的黑客还需要学习 C 语言,以便破解 Unix,以及学习 Perl 语言,以便进行系统管理和编写 cgi 脚本。最后,真正认真的黑客应该考虑学习 Lisp:

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

这与你学习拉丁语时经常听到的论点相同。学习拉丁语不会让你找到工作,除非你成为古典文学教授,但它可以提高你的思维,让你成为更好的作家,用你想要使用的语言写作,比如英语。

但是等一下。这个比喻并没有那么广泛。拉丁语不能给你带来工作,是因为没有人会说拉丁语。如果你用拉丁语写作,没有人能理解你。但是 Lisp 是一种计算机语言,而计算机会说你(程序员)告诉它们的任何语言。

所以如果 Lisp 能让你成为一个更好的程序员,就像他说的那样,你为什么不使用它呢?如果给一个画家一支能让他成为更好的画家的画笔,在我看来他会想在他的所有画作中使用它,不是吗?我并不是想在这里取笑 Eric Raymond。总的来说,他的建议很好。他对 Lisp 的看法几乎是传统观点。但传统观点中有一个矛盾:Lisp 会让你成为一个更好的程序员,但你却不会使用它。

为什么不呢?毕竟,编程语言只是工具。如果 Lisp 确实能产生更好的程序,你就应该使用它。如果不能,那谁需要它呢?

这不仅仅是一个理论问题。软件行业竞争非常激烈,容易出现自然垄断。在其他条件相同的情况下,一家能够更快更好地编写软件的公司将使其竞争对手破产。当你创办一家初创公司时,你会非常敏锐地感受到这一点。创业往往是全有或全无的。你要么致富,要么一无所获。在创业公司,如果你押注了错误的技术,你的竞争对手就会打败你。

罗伯特和我都很了解 Lisp,我们找不到任何理由不相信自己的直觉,选择 Lisp。我们知道其他人都在用 C++ 或 Perl 编写软件。但我们也知道那并不意味着什么。如果你以这种方式选择技术,你就会运行 Windows。当你选择技术时,你必须忽略其他人在做什么,只考虑什么才是最好的。

在初创公司尤其如此。在大公司,你可以做其他大公司都在做的事情。但初创公司不能做其他初创公司都在做的事情。我认为很多人没有意识到这一点,即使在初创公司也是如此。

大型公司的平均年增长率约为 10%。因此,如果你经营一家大型公司,并且按照大型公司的平均做法做每件事,那么你就能像大型公司一样做得好——也就是说,每年增长 10% 左右。

当然,如果你经营一家初创公司,也会发生同样的事情。如果你按照普通初创公司的方式做每件事,你应该期待平均表现。问题是,平均表现意味着你会破产。初创公司的存活率远低于百分之五十。所以如果你经营一家初创公司,你最好做点不同寻常的事情。否则,你就有麻烦了。

早在 1995 年,我们就明白了一点,我认为我们的竞争对手还不了解,现在也很少有人了解:当你编写的软件只需要在自己的服务器上运行,你可以使用任何你想要的语言。当你编写桌面软件时,人们倾向于使用与操作系统相同的语言来编写应用程序。十年前,编写应用程序意味着用 C 语言编写应用程序。但对于基于 Web 的软件,尤其是当你同时拥有语言和操作系统的源代码时,你可以使用任何你想要的语言。

然而,这种新的自由是一把双刃剑。既然你可以使用任何语言,你就必须考虑使用哪一种。那些试图假装一切都没有改变的公司可能会发现他们的竞争对手并没有改变。

如果你可以使用任何语言,你会使用哪种语言?我们选择了 Lisp。首先,在这个市场上,快速开发显然很重要。我们都是从零开始,因此一家能够先于竞争对手完成新功能的公司将拥有巨大优势。我们知道 Lisp 是一种非常适合快速编写软件的语言,而基于服务器的应用程序会放大快速开发的效果,因为你可以在软件完成后立即发布。

如果其他公司不想使用 Lisp,那就更好了。它可能给我们带来技术优势,我们需要尽可能多的帮助。当我们创办 Viaweb 时,我们没有任何商业经验。我们对营销、招聘、融资或获取客户一无所知。我们俩甚至都没有真正的工作。我们唯一擅长的就是编写软件。我们希望这能拯救我们。我们会利用软件部门能得到的任何优势。

因此,你可以说使用 Lisp 是一种实验。我们的假设是,如果我们用 Lisp 编写软件,我们将能够比竞争对手更快地完成功能,并且能够在我们的软件中完成他们无法完成的事情。而且由于 Lisp 非常高级,我们不需要庞大的开发团队,因此我们的成本会更低。如果是这样,我们可以用更少的钱提供更好的产品,同时仍然盈利。我们最终会获得所有用户,而我们的竞争对手将一无所获,并最终破产。无论如何,这正是我们希望发生的事情。

这次试验的结果如何?有些令人惊讶的是,它成功了。我们最终有很多竞争对手,大约有 20 到 30 个,但他们的软件都无法与我们的竞争。我们有一个所见即所得的在线商店构建器,它在服务器上运行,但感觉就像桌面应用程序。我们的竞争对手有 CGI 脚本。我们在功能方面总是远远领先于他们。有时,竞争对手会迫于无奈,试图引入我们没有的功能。但 Lisp 的开发周期非常快,有时我们可以在竞争对手在新闻稿中宣布新功能的一两天内复制它。等到报道新闻稿的记者打电话给我们时,我们也已经有了新功能。

我们的竞争对手一定认为我们拥有某种秘密武器——我们正在解码他们的 Enigma 流量之类的东西。事实上,我们确实有秘密武器,但它比他们意识到的要简单。没有人向我们泄露他们的功能。我们能够以比任何人想象的更快的速度开发软件。

九岁左右时,我偶然拿到了一本弗雷德里克·福赛斯的*《豺狼之日》* 。主人公是一名杀手,受雇刺杀法国总统。杀手必须绕过警察才能进入一间俯瞰总统行进路线的公寓。他从警察身边走过,打扮成一个拄着拐杖的老人,但他们从未怀疑过他。

我们的秘密武器也类似。我们用一种奇怪的人工智能语言编写软件,语法怪异,到处都是括号。多年来,听到人们这样描述 Lisp 让我很恼火。但现在它对我们有利了。在商业领域,没有什么比竞争对手无法理解的技术优势更有价值。在商业领域,就像在战争中一样,出其不意和武力一样有价值。

因此,说起来有点不好意思,在我们开发 Viaweb 时,我从未公开谈论过 Lisp。我们从未向媒体提及过它,如果你在我们的网站上搜索 Lisp,你只会在我的简历中找到两本书的书名。这并非偶然。一家初创公司应该尽可能少地向竞争对手提供信息。如果他们不知道我们的软件是用什么语言编写的,或者不在乎,我希望保持这种状态。[2]

最了解我们技术的人是客户。他们也不关心 Viaweb 是用什么语言编写的,但他们发现它确实很好用。它让他们可以在几分钟内建立外观精美的在线商店。因此,通过口口相传,我们获得了越来越多的用户。到 1996 年底,我们的在线商店数量约为 70 家。到 1997 年底,我们有 500 家。六个月后,当雅虎收购我们时,我们的用户数量为 1070 人。今天,作为雅虎商店,这款软件继续主导市场。它是雅虎最赚钱的部分之一,用它建立的商店是雅虎购物的基础。我于 1999 年离开雅虎,所以我不知道他们现在有多少用户,但我最后听说大约有 20,000 名用户。

布鲁布悖论

Lisp 有什么了不起的?如果 Lisp 真的那么好,为什么不是每个人都在用呢?这些问题听起来像是反问,但实际上答案很直接。Lisp 之所以如此优秀,并不是因为它具有只有爱好者才能看到的神奇品质,而是因为它是目前最强大的语言。并不是每个人都在用它的原因是,编程语言不仅仅是一种技术,也是思维习惯,没有什么比它变化得更慢了。当然,这两个答案都需要解释。

首先我想说一个令人震惊的争议性言论:编程语言的功能各不相同。

至少很少有人会质疑高级语言比机器语言更强大。如今大多数程序员都同意,你通常不想用机器语言编程。相反,你应该用高级语言编程,然后让编译器为你将其翻译成机器语言。这个想法现在甚至已经融入硬件中:自 20 世纪 80 年代以来,指令集都是为编译器而不是人类程序员设计的。

每个人都知道用机器语言手写整个程序是错误的。但人们不太理解的是,这里有一个更普遍的原则:如果你有几种语言可供选择,那么在其他条件相同的情况下,除了最强大的语言之外,用其他任何语言编写程序都是错误的。[3]

这条规则有很多例外。如果你正在编写一个程序,它必须与用某种语言编写的程序密切配合,那么用同一种语言编写新程序可能是一个好主意。如果你正在编写一个只需要做一些非常简单的事情的程序,比如数字运算或位操作,那么你不妨使用一种不太抽象的语言,特别是因为它可能稍微快一点。如果你正在编写一个简短的一次性程序,那么你最好只使用具有最佳库函数来完成任务的语言。但一般来说,对于应用软件,你希望使用你能找到的最强大(合理高效)的语言,使用任何其他语言都是一个错误,与用机器语言编程完全一样,尽管程度可能较轻。

可以看出,机器语言是低级语言。但至少作为一种社会惯例,高级语言通常被视为等同的。但事实并非如此。从技术上讲,“高级语言”一词并不意味着任何非常明确的东西。没有分界线,一边是机器语言,另一边是所有高级语言。语言沿着抽象的连续体 [4] 下降,从最强大的语言一直到机器语言,而机器语言本身的力量也各不相同。

以 Cobol 为例。Cobol 是一种高级语言,因为它会被编译成机器语言。有人会认真地争论说 Cobol 的功能与 Python 相当吗?它可能比 Python 更接近机器语言。

或者 Perl 4 怎么样?在 Perl 4 和 Perl 5 之间,该语言添加了词法闭包。大多数 Perl 黑客都会同意 Perl 5 比 Perl 4 更强大。但是一旦你承认了这一点,你就承认了一种高级语言可以比另一种更强大。并且不可避免地,除了特殊情况外,你应该使用你能得到的最强大的语言。

但这个想法很少被坚持到底。过了一定年龄后,程序员很少会主动更换语言。无论人们习惯使用哪种语言,他们往往认为只要够用就行。

程序员对他们最喜欢的语言非常执着,我不想伤害任何人的感情,所以为了解释这一点,我将使用一种名为 Blub 的假想语言。Blub 正好处于抽象连续体的中间。它不是最强大的语言,但它比 Cobol 或机器语言更强大。

事实上,我们假设的 Blub 程序员不会使用它们中的任何一个。当然,他不会用机器语言编程。那是编译器的用途。至于 Cobol,他不知道任何人如何用它完成任何事情。它甚至没有 x(您选择的 Blub 功能)。

只要我们假设的 Blub 程序员向下看权力连续体,他就知道自己在向下看。不如 Blub 强大的语言显然不那么强大,因为它们缺少他习惯的一些功能。但是当我们假设的 Blub 程序员朝另一个方向看,即权力连续体向上时,他并没有意识到自己在向上看。他看到的只是奇怪的语言。他可能认为它们在权力上与 Blub 相当,但还包含所有这些棘手的东西。Blub 对他来说已经足够好了,因为他用 Blub 思考。

然而,当我们换到使用任何一种能力等级较高的语言的程序员的角度时,我们会发现他反过来看不起 Blub。你怎么能在 Blub 中完成任何事情?它甚至没有 y。

根据归纳法,只有那些了解最强大的语言的程序员才能看到各种语言之间所有能力的差异。(这可能就是 Eric Raymond 说 Lisp 让你成为更好的程序员的意思。)你不能相信其他人的意见,因为 Blub 悖论:他们对自己使用的任何语言都感到满意,因为这决定了他们思考程序的方式。

我从自己的经历中知道这一点,当时我还是高中生,用 Basic 编写程序。这种语言甚至不支持递归。很难想象不使用递归会怎样编写程序,但我当时并不怀念它。我用 Basic 思考。而且我在这方面很擅长。我研究过的所有东西都是我的大师。

Eric Raymond 向黑客推荐的五种语言在能力上处于不同的位置。它们相对于其他语言处于什么位置是一个敏感话题。我想说的是,我认为 Lisp 处于领先地位。为了支持这一说法,我将告诉你我在研究其他四种语言时发现的缺失之一。我认为,如果没有宏,你怎么能用它们完成任何事情呢?[5]

许多语言都有一种叫做宏的东西。但 Lisp 宏是独一无二的。信不信由你,它们的作用与括号有关。Lisp 的设计者在语言中加入所有这些括号并不是为了与众不同。对于 Blub 程序员来说,Lisp 代码看起来很奇怪。但这些括号的存在是有原因的。它们是 Lisp 与其他语言之间根本区别的外在证据。

Lisp 代码由 Lisp 数据对象组成。这并不是说源文件包含字符,字符串是该语言支持的数据类型之一。Lisp 代码在被解析器读取后,由可以遍历的数据结构组成。

如果你了解编译器的工作原理,那么实际上 Lisp 的语法并不奇怪,而是 Lisp 没有语法。你在解析其他语言时在编译器内生成的解析树中编写程序。但这些解析树对你的程序来说是完全可访问的。你可以编写程序来操纵它们。在 Lisp 中,这些程序称为宏。它们是编写程序的程序。

编写程序的程序?你什么时候会想这么做?如果你用 Cobol 思考,那不会经常这么做。如果你用 Lisp 思考,那总是会这么做。如果我能给出一个功能强大的宏的例子,然后说“好了!怎么样?”,那会很方便。但如果我这样做了,对于不懂 Lisp 的人来说,它看起来就像胡言乱语;这里没有足够的空间来解释你需要知道的一切以理解它的含义。在Ansi Common Lisp中,我试图尽可能快地推进事情,即便如此,直到第 160 页我才讲到宏。

但我想我可以给出一种可能令人信服的论据。Viaweb 编辑器的源代码大概有 20-25% 是宏。宏比普通的 Lisp 函数更难编写,在不需要时使用宏被认为是不好的风格。所以代码中的每个宏都是必须存在的。这意味着这个程序中至少有 20-25% 的代码正在执行你无法用其他语言轻松完成的操作。无论 Blub 程序员对我声称的 Lisp 的神秘力量有多么怀疑,这都应该让他感到好奇。我们并不是为了自娱自乐而编写这些代码。我们是一家小型初创公司,我们尽最大努力编程,以便在我们和竞争对手之间设置技术壁垒。

怀疑论者可能会开始怀疑这其中是否存在某种关联。我们的代码中很大一部分都在做其他语言很难做到的事情。最终的软件可以做我们竞争对手的软件无法做到的事情。也许这其中存在某种联系。我鼓励你去追寻这条线索。那个拄着拐杖蹒跚而行的老人可能比表面上看到的还要多。

适合初创企业的合气道

但我并不指望能说服任何人(超过 25 岁)去学习 Lisp。本文的目的不是改变任何人的想法,而是让那些已经对使用 Lisp 感兴趣的人放心——这些人知道 Lisp 是一种强大的语言,但担心它没有得到广泛使用。在竞争激烈的情况下,这是一种优势。Lisp 的力量因竞争对手不了解它而倍增。

如果你考虑在创业公司中使用 Lisp,你不必担心它不被广泛理解。你应该希望它保持这种状态。而且很有可能。编程语言的本质就是让大多数人对他们目前使用的东西感到满意。计算机硬件的变化速度比个人习惯快得多,编程实践通常比处理器落后 10 到 20 年。在麻省理工学院等地方,他们在 20 世纪 60 年代初就用高级语言编写程序,但许多公司在 20 世纪 80 年代仍然继续用机器语言编写代码。我敢打赌,很多人继续编写机器语言,直到处理器,就像一个急于打烊回家的酒保,最终通过切换到 risc 指令集将他们踢出局。

通常来说,技术变化很快。但编程语言却不同:编程语言不仅仅是技术,也是程序员的思维方式。它们一半是技术,一半是宗教。[6] 因此,中等语言(即中等程序员使用的语言)的发展速度如冰山般缓慢。Lisp 在 1960 年左右引入的垃圾收集现在被广泛认为是一件好事。运行时类型也越来越受欢迎。Lisp 在 1970 年代初引入的词法闭包现在才刚刚进入人们的视线。Lisp 在 1960 年代中期引入的宏仍然是未知领域。

显然,中间语言具有巨大的势头。我并不是说你可以对抗这种强大的力量。我所建议的恰恰相反:就像合气道练习者一样,你可以用它来对付你的对手。

如果你在一家大公司工作,这可能并不容易。当你的老板刚刚在报纸上看到,其他语言即将像二十年前的 Ada 一样统治世界时,你将很难说服他允许你使用 Lisp 开发程序。但如果你所在的初创公司还没有这样的老板,那么你可以像我们一样,将 Blub 悖论转化为自己的优势:你可以使用竞争对手无法匹敌的技术,因为他们无法与中等语言相匹敌。

如果你发现自己正在为一家初创公司工作,这里有一个评估竞争对手的实用技巧。阅读他们的招聘信息。他们网站上的其他内容可能是库存照片或类似的文字,但招聘信息必须具体说明他们想要什么,否则他们会招到错误的候选人。

在 Viaweb 工作的那几年里,我读了很多职位描述。似乎每个月都会有新的竞争对手出现。我做的第一件事,就是查看他们是否有在线演示,然后看看他们的招聘信息。几年后,我就能分辨出哪些公司值得担心,哪些公司不必担心。职位描述越是 IT 化,公司就越不危险。最安全的是那些需要 Oracle 经验的公司。你永远不必担心这些。如果他们说需要 C++ 或 Java 开发人员,你也是安全的。如果他们需要 Perl 或 Python 程序员,那就有点可怕了——这听起来像是一家至少技术方面由真正的黑客管理的公司。如果我看到招聘 Lisp 黑客的招聘信息,我会非常担心。

笔记

[1] Viaweb 最初由两部分组成:编辑器,用 Lisp 编写,人们用它来建立网站;订购系统,用 C 编写,用于处理订单。第一个版本主要是 Lisp,因为订购系统很小。后来我们又添加了两个模块,一个用 C 编写的图像生成器,以及一个主要用 Perl 编写的后台管理器。

2003 年 1 月,雅虎发布了用 C++ 和 Perl 编写的新版编辑器。不过,很难说这个程序是否不再用 Lisp 编写,因为要把这个程序翻译成 C++,他们实际上必须编写一个 Lisp 解释器:据我所知,所有页面生成模板的源文件仍然是 Lisp 代码。(请参阅Greenspun 的第十条规则。)

[2] 罗伯特·莫里斯 (Robert Morris) 说我没必要保密,因为即使我们的竞争对手知道我们在使用 Lisp,他们也不会明白为什么:“如果他们真的那么聪明,早就用 Lisp 编程了。”

[3] 从图灵等价的角度来看,所有语言都同样强大,但这并不是程序员关心的词义。(没有人想编写图灵机。)程序员关心的那种能力可能无法正式定义,但一种解释是,它指的是你只能通过为功能更强大的语言编写解释器才能在功能较弱的语言中获得的功能。如果语言 A 有一个从字符串中删除空格的运算符,而语言 B 没有,那么这可能不会使 A 更强大,因为你可能可以在 B 中编写一个子程序来执行此操作。但是,如果 A 支持递归,而 B 不支持,那么你不太可能通过编写库函数来解决这个问题。

[4] 书呆子们请注意:或者可能是个格子,向上逐渐变窄;这里重要的不是形状,而是至少存在部分顺序的想法。

[5] 将宏视为一个单独的特性有点误导。实际上,它们的实用性因其他 Lisp 特性(如词法闭包和剩余参数)而大大增强。

[6] 因此,编程语言之间的比较要么是宗教战争,要么是本科教科书,这些教科书如此坚定地保持中立,以至于它们实际上是人类学著作。那些珍视和平或想要终身教授职位的人会避开这个话题。但这个问题只有一半是宗教问题;其中有些东西值得研究,特别是如果你想设计新的语言。