击败平均
原文:Beating the Averages
作者:Paul Graham 发表:2001-04
译者:Claude(baoyu-translate)
2001 年 4 月,2003 年 4 月修订
(本文由 2001 年 Franz 开发者研讨会(Franz Inc. 是商业 Common Lisp 厂商)的一次演讲改写。)
1995 年夏天,我和朋友 Robert Morris(PG 在 Viaweb 的合伙人)一起创办了一家叫 Viaweb(PG 1995 年创立的电商建站公司,后被雅虎收购)的创业公司。我们的计划是写一套软件,让最终用户能搭建自己的在线商店。这套软件在当时的新颖之处在于:它跑在我们的服务器上,用普通的网页当界面。
当然,那个时候有这个想法的人可能不止我们。但据我所知,Viaweb 是第一个基于 Web 的应用。我们觉得这个点子太新,索性就用它给公司起了名字:Viaweb,意思是我们的软件通过 Web 工作,而不是跑在你桌面上的电脑里。
这套软件还有一个不寻常的地方:它主要是用 Lisp 写的。它是最早一批用 Lisp 写成的大型最终用户应用之一。在那之前,Lisp 主要还是大学和研究所里的东西。[1]
秘密武器
Eric Raymond 写过一篇文章叫《如何成为黑客》,里面除了别的内容,他还告诉想当黑客的人应该学哪些语言。他建议从 Python 和 Java 入手,因为容易学。认真的黑客还会想学 C,为了 hack Unix;学 Perl,为了系统管理和 CGI 脚本。最后,真正认真的黑客应该考虑学 Lisp:
Lisp 值得学,因为当你最终领悟它时,你会获得一种深刻的开悟体验;这种体验会让你余生都成为更好的程序员,哪怕你以后并不真的常用 Lisp 本身。
这跟人们劝你学拉丁文的论调一样。它不会帮你找到工作,除非你想当古典学教授;但它会让你的脑子变好,让你在你真正想用的语言——比如英语——里写得更好。
可等一下。这个比喻撑不到那么远。拉丁文找不到工作,是因为没人讲拉丁文。你用拉丁文写出来,没人能看懂。但 Lisp 是一门计算机语言,而计算机能听懂的,是你这个程序员告诉它讲的任何语言。
所以,如果像他说的那样,Lisp 能让你成为更好的程序员,你为什么不愿意用它?如果有人给一位画家一支画笔,能让他画得更好,他不是会想在所有画里都用这支笔吗?我并不是要拿 Eric Raymond 开玩笑。整体上他的建议是好的。他对 Lisp 的看法基本就是主流共识。但这个主流共识里有个矛盾:Lisp 会让你成为更好的程序员,可你就是不会用它。
为什么不?编程语言说到底就是工具。如果 Lisp 真能写出更好的程序,你就该用它。如果不能,那谁还需要它?
这不只是个理论问题。软件是一门竞争极其激烈的生意,而且天生倾向于自然垄断。其他条件相同的情况下,能更快、更好地写出软件的公司,会把对手赶出局。当你正在做一家创业公司时,这件事感受尤为锋利。创业公司本质上是一锤子买卖:要么发财,要么一无所有。在创业公司里,如果你赌错了技术,对手会把你碾碎。
Robert 和我都熟悉 Lisp,我们看不出有什么理由不相信自己的直觉、不去用 Lisp。我们知道别人都在用 C++ 或 Perl 写软件。但我们也知道,这并不说明什么。如果你按这种方式选技术,你现在用的就是 Windows。选技术的时候,你必须无视别人在做什么,只考虑哪种最管用。
这一点对创业公司尤其成立。在大公司里,你可以做所有大公司都在做的事。但创业公司不能做所有创业公司都在做的事。我觉得很多人没意识到这一点,连身在创业公司里的人也常常没意识到。
普通大公司一年增长大约百分之十。所以如果你在跑一家大公司,并且事事都按平均的大公司那样来,你能预期的成绩就是平均水平——一年增长百分之十左右。
跑创业公司也一样。如果你做事方式都和平均的创业公司一样,那你能预期到平均的表现。问题是,平均表现意味着你会倒闭。创业公司的存活率远低于一半。所以如果你在跑一家创业公司,最好做点不太一样的事。否则就麻烦了。
回到 1995 年,我们当时清楚一件事,我觉得我们的对手没搞懂,连现在很多人也没搞懂:当你写的软件只需要在自己的服务器上跑时,你想用什么语言都行。写桌面软件时,有一种很强的偏向——用和操作系统一样的语言写应用。十年前,写应用就是用 C 写应用。但到了基于 Web 的软件,特别是当你既有语言的源码又有操作系统的源码时,你想用什么语言都行。
不过,这种新的自由是把双刃剑。既然现在你可以用任何语言,你就必须想清楚到底用哪一种。试图假装什么都没变的公司,得冒一个风险——它们的对手不会假装。
如果你可以用任何语言,你用哪一种?我们选了 Lisp。一来很明显,在这个市场里快速开发会很重要。我们都是从零开始,谁能比对手更早做出新功能,谁就有大优势。我们知道 Lisp 是写软件特别快的语言;而服务器端应用又会放大快速开发的效果——东西做好那一刻就能发布。
如果别的公司不想用 Lisp,那就更好。这能给我们一点技术上的优势,而我们什么帮助都需要。Viaweb 起步时,我们一点商业经验也没有。我们对市场营销、招人、融资、拉客户一无所知。我们俩谁都没做过称得上“正经工作“的工作。我们唯一擅长的就是写软件。我们希望靠这一点能救命。在软件这一栏里,任何能拿到的优势我们都要。
所以可以说,用 Lisp 是一次实验。我们的假设是:如果用 Lisp 写软件,我们做新功能会比对手快,并且能在软件里做出他们做不出来的东西。又因为 Lisp 高度抽象,我们不需要一个庞大的开发团队,所以成本会更低。如果这一切成立,我们就能用更少的钱提供更好的产品,同时还赚钱。最终所有用户都会到我们这儿来,对手一个用户也拿不到,迟早倒闭。这就是我们当时希望发生的事。
实验的结果如何?多少有点意外——它真的成功了。最后我们大约有二三十家对手,但他们的软件没一个能和我们竞争。我们有一个所见即所得(WYSIWYG)的在线商店搭建器,跑在服务器上,用起来却像桌面应用。我们的对手用的是 CGI 脚本。在功能上我们一直把他们甩得远远的。有时候对手急了,会试图推出我们没有的功能。但用 Lisp 的话,我们的开发周期太快,有时候对手新功能在新闻稿里宣布之后一两天,我们就把同样的功能也做出来了。等报道这条新闻稿的记者来电话采访我们时,我们也已经有这个新功能了。
我们的对手肯定觉得我们有什么秘密武器——好像我们在破译他们的 Enigma(二战德军密码机)密文。事实上我们确实有秘密武器,只是比他们想的简单。没人在向我们泄露他们功能的消息。我们只是能比所有人以为可能的速度更快地写出软件。
我大概九岁那年,碰巧弄到了一本 Frederick Forsyth 的《豺狼的日子》。主角是一个职业杀手,受雇暗杀法国总统。他得绕过警察,进到一间俯瞰总统行经路线的公寓。他扮成一个拄拐杖的老人,从警察身边大摇大摆走过,没人怀疑他。
我们的秘密武器跟这个差不多。我们用的是一门怪怪的“AI 语言“,语法古怪,到处是括号。多年来听到别人这样形容 Lisp,让我很不耐烦。但这下却成了我们的优势。在商业里,没什么比一个对手不理解的技术优势更值钱。商场和战场一样,出其不意值得抵得过实力。
所以——我说出来都有点不好意思——做 Viaweb 那段时间,我从来没在公开场合提过 Lisp。我们对媒体只字不提。如果你在我们网站上搜 “Lisp”,你能找到的全部就是我个人简介里两本书的书名。这不是巧合。一家创业公司应该让对手得到尽可能少的信息。如果他们不知道我们的软件是用什么语言写的,或者根本不在乎,我希望事情就保持那样。[2]
最了解我们技术的,其实是客户。他们也不在乎 Viaweb 是用什么语言写的,但他们注意到它真的很好用。它让他们字面意义上几分钟之内就能搭出一家漂亮的在线商店。靠口口相传,我们的用户越来越多。1996 年底,我们在线大约有 70 家商店。1997 年底有 500 家。再过半年,雅虎收购我们时,我们有 1070 个用户。今天,作为 Yahoo Store,这套软件继续主导这个市场。它是雅虎里更赚钱的几块业务之一,用它搭出的商店是 Yahoo Shopping 的基石。我 1999 年离开雅虎,所以我不太清楚他们现在到底有多少用户,但我最后听到的数字是大约两万家。
Blub 悖论
Lisp 到底好在哪?如果 Lisp 这么好,为什么大家都不用?这听起来像是设问,其实有直接的答案。Lisp 这么好,并不是因为它有什么只有信徒才看得见的魔法,而是因为它就是手头能拿到的最强大的语言。而大家不用它,是因为编程语言不只是技术,它同时是思维习惯,而思维习惯是变化最慢的东西。当然,这两个回答都需要解释。
我先抛一个惊人地有争议的论断:编程语言在能力上有强弱之分。
至少,没什么人会反对:高级语言比机器语言更强。今天大多数程序员都会同意,正常情况下你不该用机器语言来写程序。你应该用一种高级语言来写,让编译器帮你翻译成机器语言。这个思想现在甚至被烙进了硬件:从 1980 年代起,指令集就是为编译器而不是为人类程序员设计的。
大家都知道完全用机器语言手写整个程序是个错误。可比较少人理解的是,这里其实有一个更普遍的原则:如果你有几种语言可选,那么在其他条件相同的情况下,用那门最强的语言以外的任何一种来写都是个错误。[3]
这条规则有许多例外。如果你写的程序必须和某种语言写的程序紧密协作,那么用同一种语言写新程序可能是好主意。如果你写的程序只做某件非常简单的事,比如数值计算或者位操作,你不妨用一门抽象层次更低的语言,特别是它可能稍微快一点。如果你写的是一段短的、写完就扔的程序,那也许干脆就用对这件事库函数最齐全的那门语言。但总的来说,对于应用软件,你想用的是你能拿到的最强(在效率合理范围内)的语言;用别的,就是错误,性质和用机器语言写程序完全一样,只是程度可能轻一些。
你可以看到,机器语言层次非常低。但是——至少作为一种社会习俗——所有高级语言常常被一律视为等价的。它们并不等价。从技术角度讲,“高级语言“这个词本身没有什么明确含义。并不是说一边是机器语言,另一边是所有的高级语言、中间有一条分界线。语言落在一条抽象度的连续谱[4]上,从最强的一直到机器语言;机器语言彼此之间能力也不一样。
考虑一下 Cobol。Cobol 是高级语言——意思是它会被编译成机器语言。可有谁会认真说 Cobol 在能力上等同于、比方说,Python?它大概离机器语言更近一点。
或者再看 Perl 4。Perl 4 到 Perl 5 之间,词法闭包被加进了语言。多数 Perl 黑客都会同意 Perl 5 比 Perl 4 更强。可一旦你承认了这一点,你就承认了一种高级语言可以比另一种高级语言更强。由此不可避免地推出:除了特殊情形,你应当用你能拿到的最强的那一门。
不过这个想法很少被推到底。过了某个年纪,程序员就很少主动换语言。人们用什么习惯了,就倾向于觉得那个“够用了“。
程序员对自己钟爱的语言极其有感情,我也不想伤任何人的感情,所以为了说清楚这一点,我打算用一门假想的语言,叫它 Blub(PG 假想的“中等强度“语言)。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 里,你直接就在这些抽象语法树上写程序。而且这些语法树对你的程序是完全开放的。你可以写程序去操作它们。在 Lisp 里,这种程序叫宏。它们是写程序的程序。
写程序的程序?什么时候会想干这种事?如果你用 Cobol 思考,那不太常想。如果你用 Lisp 思考,那一直想。这里要是能给你举一个有力的宏的例子,然后说:“看,怎么样?“就好了。可如果我真这么干,对不懂 Lisp 的人来说就是天书;这里也没有篇幅把你需要懂的所有背景讲清楚。我在《Ansi Common Lisp》里已经尽可能地往前赶了,即便如此,宏也是到第 160 页才讲到的。
不过我想我可以给一个或许有说服力的论据。Viaweb 编辑器的源代码大概有 20%–25% 是宏。宏比普通的 Lisp 函数更难写,而且在不必要时使用宏被认为是糟糕的风格。所以那段代码里每一个宏,都是因为非这么写不可才存在的。也就是说,这个程序至少有 20%–25% 的代码,在做的是别的语言里不容易做的事。Blub 程序员对我宣称的 Lisp 那些神秘力量再怀疑,看到这一点也该好奇了。我们写这些代码不是为了自娱自乐。我们是一个小小的创业团队,拼命编程,目的只有一个——在我们和对手之间筑起技术壁垒。
爱多想的人也许会开始怀疑,这里头是不是有些相关性。我们很大一块代码做的是别的语言里很难做的事。结果做出来的软件能做对手做不出的事。也许这之间真有某种联系。我建议你顺着这条线想下去。那个拄拐慢慢挪的老头,可能不像表面看上去那么简单。
创业者的合气道
但我并不指望能说服任何人(25 岁以上的)跑出去学 Lisp。这篇文章的目的不是改变谁的想法,而是让那些已经对用 Lisp 感兴趣的人放心——那些知道 Lisp 是一门强大的语言,却又因为它没被广泛使用而担心的人。在竞争场景下,这恰恰是优势。Lisp 的力量被你的对手不懂这一事实又乘了一遍。
如果你在考虑创业时用 Lisp,你不该担心它没被广泛理解。你应该希望它继续如此。它很可能就会一直如此。编程语言的本性是,让大多数人对自己当下用的那种感到满意。计算机硬件的变化比个人习惯快太多了,所以编程实践通常落后处理器十到二十年。在 MIT 这种地方,他们 1960 年代初就已经在用高级语言写程序,可许多公司直到 1980 年代还在用机器语言写代码。我猜很多人会一直用机器语言写下去,直到处理器像急着关门回家的酒保那样,靠着切换到 RISC 指令集,把他们撵了出去。
通常,技术变得很快。但编程语言不一样:编程语言不只是技术,还是程序员思考所用的东西。它一半是技术,一半是宗教。[6]因此,“中位语言”——也就是中位程序员所用的那门语言——动得像冰山一样慢。垃圾回收,Lisp 大约 1960 年引入,今天才被广泛认为是好东西。运行时类型也类似,正在流行起来。词法闭包,Lisp 1970 年代初引入,今天勉强出现在雷达屏幕上。宏,Lisp 1960 年代中期引入,至今仍是 terra incognita(拉丁语:未知之地)。
显然,中位语言有巨大的惯性。我并不是建议你跟这股强大的力量正面硬刚。我建议的恰好相反:像合气道(Aikido)的练习者一样,你可以借这股力量来对付对手。
如果你在大公司里干,这可能不容易。当你的尖头老板刚在报上看到某种语言“蓄势待发,要像二十年前的 Ada 那样统治世界“时,你很难说服他让你用 Lisp 来做东西。可如果你是在一家还没有尖头老板的创业公司里干,你就可以像我们当年那样,把 Blub 悖论变成你的优势:你可以使用对手——他们牢牢被中位语言粘住,动弹不得——永远跟不上的技术。
如果你哪天真的进了一家创业公司,这里有个评估对手的实用小技巧:去读他们的招聘启事。他们网站上的其他东西可能都是库存照片或库存照片的文字版,但招聘启事必须把他们要什么人写得很具体,否则他们招到的人就不对路。
做 Viaweb 的那几年里,我读了很多招聘描述。新对手好像差不多每个月都从某个角落冒出来。我会做的第一件事,确认他们有没有可用的在线 demo 之后,就是去看他们的招聘启事。这么干了几年,我大致就能判断哪些公司值得担心、哪些不必。招聘描述越是 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:“如果他们真有那么聪明,他们早就在用 Lisp 写程序了。”
[3] 在图灵等价的意义上,所有语言能力都一样——但程序员关心的并不是这个意义。(没人想去给图灵机写程序。)程序员关心的那种能力,也许在形式上不可定义,但有一种方式可以解释它:它指的是这样一些特性——你只能通过在弱语言里写一个强语言的解释器,才能在弱语言中得到。如果语言 A 有一个把字符串里空格去掉的算子,而语言 B 没有,这大概并不会让 A 更强,因为你大概可以在 B 里写一个子程序来做这件事。但如果 A 支持,比如说,递归,而 B 不支持,这就不太可能靠写库函数来弥补了。
[4] 给较真的同学:或者也可以是个格,越往上越窄;这里重要的不是形状,而是“至少存在一个偏序“这件事。
[5] 把宏当作一项独立的特性来谈,是有点误导的。实际上宏的有用程度被 Lisp 的其他特性——比如词法闭包和剩余参数——大大放大了。
[6] 结果就是,编程语言之间的比较,要么变成宗教战争,要么变成那种刻意保持中立到几乎成了人类学著作的本科教材。爱惜羽毛或者想拿终身教职的人,都避开这个话题。但这个问题只有一半是宗教问题;这里头有些东西值得研究——尤其是当你想设计新语言的时候。