描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787115249494
世界上**个互联网应用程序Viaweb开发者
举世公认的互联网创业权威Paul Graham的文集!
Paul Graham带领我们探究黑客的世界,了解黑客的爱好和动机
Paul Graham旁征博引历史事件,妙笔生花
《黑客与画家》从书名我们都能看出书中的内容并不深奥,作者**的目的就是,通过这本书让普通读者理解我们所处的这个计算机时代,揭示它的发展轨迹,帮助你看清我们现在的位置和将来的方向。
本书是硅谷创业之父PaulGraham的文集,主要介绍黑客即优秀程序员的爱好和动机,讨论黑客成长、黑客对世界的贡献以及编程语言和黑客工作方法等所有对计算机时代感兴趣的人的一些话题。书中的内容不但有助于了解计算机编程的本质、互联网行业的规则,还会帮助读者了解我们这个时代,迫使读者独立思考。 本书适合所有程序员和互联网创业者,也适合一切对计算机行业感兴趣的读者。
目 录
1. 为什么书呆子不受欢迎 1
他们的心思在别的地方。
2. 黑客与画家 18
黑客也是创造者,与画家、建筑师、作家一样。
3. 不能说的话 34
如果你的想法是社会无法容忍的,你怎么办?
4. 良好的坏习惯 52
与其他美国人一样,黑客的成功秘诀就是打破常规。
5. 另一条路 59
互联网软件是微机诞生后的机会。
6. 如何创造财富 90
致富的好方法就是为社会创造财富。创造财富的好方法就是创业。
7. 关注贫富分化 111
“收入分配不平等”的危害,会不会没有我们想的那样严重?
8. 防止垃圾邮件的一种方法 124
不久前,许多专家还认为无法有效地过滤垃圾邮件。本文改变了他们的想法
。
9. 设计者的品味 133
如何做出优秀的东西?
10. 编程语言解析 148
什么是编程语言?为什么它们现在很热门?
11. 一百年后的编程语言 156
一百年后,人类怎样编程?为什么不从现在开始就这样编程呢?
12. 拒绝平庸 169
别忘了你的对手与你一样,能用任何想用的语言编写互联网软件。
13. 书呆子的复仇 180
在高科技行业,只有失败者采用“业界实践”。
14. 梦寐以求的编程语言 198
一种好的编程语言,是让黑客可以随心所欲使用的语言。
15. 设计与研究 213
研究必须是“新”的,而设计必须是“好”的。
志谢 219
术语解释 221
图片授权说明 236
“此书将迫使你重新思考计算机编程的本质。”
—— Robert Morris,麻省理工学院副教授,世界上首个互联网应用程序Viaweb开发人之一
“人类社会还没有充分理解程序员带来的美和智慧。Graham的这本书却做到了这一点,描述得清晰又动人,任何愿意倾听的人都会大有收获。如果我们不愿意马上就失去一些重要的东西,那么我们这个社会就应该学会倾听。”
—— Lawrence Lessig,斯坦福大学法学院教授
“这是真正睿智之士的思维激荡,读来令人着迷。单单是‘为什么书呆子不受欢迎’一文就值得你买这本书了,它回答了我们这个时代的一个关键问题。”
—— Chris Anderson,《连线》杂志主编
“PaulGraham高瞻远瞩,文笔优雅清晰,且不乏幽默,这不仅在黑客群体中实属罕见,甚至而且足以跻身于优秀作家的行列。”
—— David Weinberger,Cluetrain Manifesto作者
“PaulGraham的《黑客与画家》是一本内容广泛的书,但是重要的地方不是你能从中知道为什么书呆子在高中时备受挫折,或者计算机语言设计和实现有什么奥妙,而是他在论述每一个题目时采取的那种方法,那样生动有趣,富有启迪性,让你莞尔一笑,然后陷入思考。强烈推荐此书给所有读者。”
—— Rob “CmdrTaco” Malda,Slashdot.org创始人、负责人
“PaulGraham是一名黑客,一位画家,还是个出色的作家。他的文章清晰易懂、幽默生动,从艺术、科学、商业互相交织的角度谈论如何写出优秀的代码,充满了与众不同的看法和切实可行的高见。你甚至可能因为看了他的文章而改用Lisp编程哦!”
—— Andy Hertzfeld,苹果机发明人之一
“这是我近读到的发人深思的一本书。行文明白流畅,主题多样,而且风趣幽默。”
—— Jeff “hemos” Bates,OSDN负责人、Slashdot.org维护者
设计者的品味
哥白尼不认同托勒密的体系,一个极其重要的原因是,他觉得托勒密提出的偏心等距点(equant)毫无美感……
——托马斯·库恩,《哥白尼革命》
我们所有人都受到凯利·约翰逊 的影响,狂热地相信外观优美的飞机一定会飞得同样漂亮。
——本·里奇,《臭鼬计划》
美感是道关卡。丑陋的数学在世界上无法生存。
——G. H. 哈代,《一个数学家的道歉》
近,我与一个在MIT教书的朋友交谈。他的研究领域很热门,每年申请他的研究生的人多得让他应付不过来。“很多人看上去很聪明,”他说,“但是我不知道他们的品味如何。”
品味。如今很少听到这个词了,人们往往使用别的叫法,但它却的的确确是我们离不开的基本概念。我的朋友的意思是,他想要的学生不仅应该技术过硬,还应当能够使用技术做出优美的产品。
数学家会把出色的工作称赞为“优美的”。无论古今,科学家、工程师、音乐家、建筑师、设计师、作家、画家都是这样做的,他们都使用同一个词。这仅仅是巧合吗,还是他们之间有共识?如果真的有共识,那么我们能不能将某一个领域发现的“美”的规律运用于另一个领域呢?
对于我们设计师来说,美就不仅仅是一个理论问题了。如果世界上真有“美”存在,我们需要能够认出它。设计产品时,我们需要良好的品味。与其把“美”说成一个虚无缥缈的抽象概念,还不如让我们考虑一个实际的问题(这样就能避免喋喋不休的空谈):如何才能做出优美的产品?
如果你在当今社会提到“品味”,很多人会对你说“品味是主观的”。他们真的就是这么认为的。喜欢一件东西,却不知道为什么自己喜欢它,原因可能是这件东西是美的,但也可能因为他们的母亲也拥有同样的东西,或者杂志上某个明星使用它,或者仅仅因为它的价格很昂贵。人类的思想就是没有经过整理的无数杂念的混合。
我们大多数人从孩提时代起就被鼓励不要去分析清楚自己的头脑。如果你的小弟弟画图时把人都涂成绿色,你想取笑他,你妈妈很可能会对你说:“你有你喜欢的方式,他有他喜欢的方式。”
你妈妈这时不是教给你什么是美学,而只是想阻止你们两个争吵。
就像大人哄小孩的其他话一样,这句话也是模棱两可的,与其他话会发生冲突。大人教导你说品味只是每个人的偏好而已。但是来到博物馆,他们却对你说,仔细观赏达·芬奇的作品,因为他是伟大的艺术家,品味超凡。
小孩子受到这样的教导会怎么想?他会怎么理解“伟大的艺术家”?这么多年来,别人无数遍地告诉他,品味就是一种偏好,是每个人自己的事情,所以他不可能直接就明白,所谓“伟大的艺术家”就是这个人的作品要比其他人的杰出。他更可能觉得,所谓“伟大的艺术家”只是针对我个人世界而言的,就是很符合我自己口味的艺术家,好比某本书上说食用西兰花对我的健康有利,所以我就应该喜欢吃西兰花一样。
把品味说成个人的偏好可以有效地杜绝争论,防止人们争执哪一种品味更好。但是问题是,这种说法是不正确的。只要你自己开始动手设计东西,就能明白这一点。
不管每个人的工作是什么,他们内心里都有一种愿望——把自己的工作做好。足球运动员想赢得比赛,CEO想增加利润。做好自己的工作会真正令人感到自豪和愉快。但是,如果你是一个设计师,并且你不承认有一种人们共同认可的东西叫做“美”,那么你就没有办法做好工作。如果品味只是一种个人偏好,那么每个人都是完美无缺的:你喜欢自己看上的东西,那就足够了。
就像别的工作一样,只要你不断地从事设计工作,你就会做得越来越好。你的品味会出现变化,你会像别人一样有所提高。如果这样的话,那么你以前的品味就不只是与现在不同,而是不如现在的好。因此,所谓的“品味没有好坏之分”的公理也就顿时见鬼去了。
现在流行“相对主义”,即认为真理是相对的。即使你已经从小孩变成了成年人,这种观点依然可能妨碍你思考“品味”。但是,只要你走出狭隘的自我,至少在心里对自己说,确实存在比其他设计更好的杰出设计,那么你就能开始仔细研究了。你的品味是如何变化的?什么原因使你做出不好的设计?其他人对设计是什么观点?
只要你开始思考这些问题,你就会发现,众多不同学科对“美”的认识有着惊人的相似度。优秀设计的原则是许多学科的共同原则,一再反复地出现。
好设计是简单的设计。从数学领域到绘画领域,你都可以听到这种说法。在数学中,它表示简短的证明往往是更好的证明。特别是对于数学公理来说,少即是多。在编程中,这种说法也基本适用。对于建筑师和设计者,它意味着美依赖于一些精心选择的结构性元素,而不依赖于表面装饰品的堆砌。(装饰品本身并不是坏事,只有当它被用来掩盖结构的苍白时,才变成了一件坏事。)绘画也是类似的,认真观察的、非常有代表性的静物作品往往要比表面极尽华美、但是实质上只是无意义重复的“巨作”(比如再现非常复杂的花边的绘画作品)更有价值。在写作上,这种说法意味着只说必须要说的话,并且说得简短。
这样强调简单似乎有点奇怪。有人会说,简单就是事物本来的样子,装饰反而意味着更多的工作。但是,当人们自己从事创造性工作的时候,好像就会忘了保持简单这个原则。刚开始写作的人喜欢用浮夸的语调,根本不像他们平时说话的样子。设计师喜欢用波浪式卷曲表现他们的艺术感。画家发现自己都是表现主义者(expressionist)。这些装饰都是花架子,在作家的长句、画家“表现主义”的画笔之下,根本就是空洞无物,表面的装饰掩盖了内部的空虚,太可怕了。
当你被迫把东西做得很简单时,你就被迫直接面对真正的问题。当你不能用表面的装饰交差时,你就不得不做好真正的本质部分。
好设计是永不过时的设计。只要没有错误,每一个数学证明都是永不过时的。所以,数学家哈代才会说:“丑陋的数学在世界上无法生存。”他的意思与飞机设计师凯利·约翰逊的观点是一样的:如果解决方法是丑陋的,那就肯定还有更好的解决方法,只是还没有发现而已。
以永不过时作为目标是一种帮助自己找到答案的方法:如果你不愿别人的答案取代你的答案,你就只好自己做出答案。某些大师的作品太过杰出,永不过时,使得后人几乎难以在该领域立足。自从16世纪出现了德国雕刻大师杜勒(Dürer),后世的雕刻家都因为自己的作品被拿来与他的作品作比较而苦不堪言。
以永不过时作为目标也是一种避开时代风潮的影响的方法。“风潮”这个词,从字面上就可以看出,它就是一阵风似的,随着时间经常改变。如果一件东西长盛不衰,那么它的吸引力一定来自本身的魅力,而不是来自风潮的影响。
说来奇怪,如果你希望自己的作品对未来的人们有吸引力,方法之一就是让你的作品对上几代人有吸引力。我们很难猜想未来是什么样子,但是可以肯定,未来的人们不会在乎今天流行的风潮,这一点与上几代人是相同的。所以,如果你的作品对今天的人们以及1500年的人都有吸引力,那么它极有可能也会吸引2500年的人。
好设计是解决主要问题的设计。厨房的煤气灶有四个出火口,排成一个正方形。每个出火口都由一个调节器控制,四个出火口就有四个调节器。请问应该如何摆放调节器?简单的摆放方法当然是把四个调节器排成一列,但要是这样做,人们使用起来就很不方便,每次都要停下来想一下到底每个调节器对应的是哪个出火口。如果直接把调节器排成与出火口一样的正方形,就不会有这个问题了。
许多坏设计做得很辛苦,但是从一开始方向就错了。20世纪中期,有一股使用无衬线(sans-serif)字体的潮流。这一类字体接近于纯手写的样式,但是它无助于解决主要的问题。印刷出来的文字首先应该是易于辨认的,所以能够清晰地分辨字母就是主要的问题。传统的新罗马(TimesRoman)字体是一种有衬线的字体,虽然看上去古老得就像维多利亚女王时代的风格,但是它的小写g就是可以很轻易地与小写y区分。
答案可以不断改进,同样,问题本身也可以不断改进。软件的难题通常可以被改成等价的较易解决的形式。历史上,物理学的主要难题曾经一度是如何诠释经典著作,后来逐渐变成对可观测到的行为进行预测,这种转变使得物理学的发展速度大大加快。
好设计是启发性的设计。英国女作家简·奥斯汀的作品几乎不带有任何描述。她不告诉读者每件东西看上去是什么样子,只是把故事讲得非常生动,让读者自己把一切都想象出来。同样,绘画作品也分为描述性绘画和启发性绘画,后者往往比前者更引人入胜。每个人看到《蒙娜丽莎》都有自己的理解。
在建筑学和设计学中,这条原则意味着,一幢建筑或一个物品应该允许你按照自己的愿望来使用。举例来说,一幢好的建筑物应该可以充当平台,让你想怎么布置就可以怎么布置,过上自己想过的家庭生活,而不是使得你像执行程序一样只能过上建筑师为你安排的生活。
在软件业中,这条原则意味着,你应该为用户提供一些基本模块,使得他们可以随心所欲自由组合,就像玩乐高积木那样。在数学中,这条原则意味着,一个可以成为许多新工作基础的证明要优于一个难度很高、但无助于未来学科发展的证明。在科学领域中,总体上可以把引用次数看作对他人启发性大小的粗略指标。
好设计通常是有点趣味性的设计。这条原则可能不是所有情况下都成立。但是,杜勒的雕刻、芬兰设计师沙里宁(Saarinen)的子宫椅(WombChair)、意大利罗马的万神殿(Pantheon)、保时捷911型汽车的原型设计(图9-1),在我看来都很有趣。逻辑学家哥德尔(G·del)的不完备定理就好像一个玩笑那样有意思。
图9-1 保时捷911E,1973年产
我想,这是因为幽默一定程度上反映了力量。幽默感是强壮的一种表现,始终拥有幽默感就代表你对厄运一笑了之,而丧失幽默感则表示你被厄运深深伤到。所以,强壮的标志(或者至少是特点)就是轻松面对自己的人生。充满自信的人常常像燕子一样,以一种居高临下的姿态轻盈地看待周围的一切,比如希区柯克拍摄的电影、16世纪画家布勒哲尔(Bruegel)的绘画(甚至莎士比亚也是一个这方面的例子)。
好的设计并非一定要有趣,但是很难想象完全无趣的设计会是好的设计。
好设计是艰苦的设计。如果观察那些做出伟大作品的人,你会发现他们的共同点就是工作得非常艰苦。如果你工作得不艰苦,你可能正在浪费时间。
困难的问题需要艰巨的付出才能解决,高难度的数学证明需要结构非常精细的解决方法(它们往往做起来很有趣),工程学也是如此。
当你攀登高山时,必须扔掉一切不必要的装备。在困难地点或预算不足的条件下,建筑师就只能做出很简练的设计。当解决难题成为压倒一切的任务时,那些流行样式与华丽装饰就被抛到一边去了。
并非所有的艰苦都是值得的。世界上有有益的痛苦,也有无益的痛苦。你需要的是咬牙向前冲刺的痛苦,而不是脚被钉子扎破的痛苦。解决难题的痛苦对设计师有好处,但是对付挑剔的客户的痛苦或者对付质量低劣的建材的痛苦就是另外一回事了。
在绘画上,肖像画通常占据地位。这不是偶然的,原因不仅是面部肖像比其他题材更能打动人,还因为我们太擅长观察脸,所以肖像画家不得不加倍努力才能达到我们的要求。如果画的是树,树枝画偏了五度也不会有人发现。但是,如果你把别人的眼睛画偏了五度,人们一眼就能看出来。
德国包豪斯(Bauhaus)学派的设计师采纳了美国建筑师路易斯·沙利文(LouisSullivan)的观点“功能决定形式”(form followsfunction),但是他们实际上的理解是“功能应当决定形式”。真实情况是,如果开发“功能”非常艰难,那么“形式”将不得不全部都由“功能”决定,因为没有多余的精力再来单独开发“形式”了。人们常常觉得野生动物非常优美,原因就是它们的生活非常艰苦,在外形上不可能有多余的部分了。
好设计是看似容易的设计。优秀运动员比赛时,让人觉得他轻轻松松就获胜了,优秀设计师也是如此,他们的工作看上去很容易。大多数时候,这是一种错觉。作家的文章读起来流畅自如,但是背后其实经过了反复修改。
科学和工程学的一些重大的发现在形式上往往很简单,会使得你觉得自己也想到过。可是,如果它真的那么简单,为什么发现人不是你呢?
达·芬奇的有些肖像画只是几根线条。看着它们,你会想只要把这十根八根线条放对位置,你也能画出如此优美的肖像画。说的没错,可是难就难在找出正确的位置。只要位置偏移一点点,整幅作品就会一溃千里。
白描其实是难画的视觉媒介,因为它们要求几近完美的再现。用数学语言说,线条属于闭合解(closed-formsolution),水平不够的艺术家没有办法直接解决问题,只能通过不断逼近来求解。许多孩子在十岁左右放弃了绘画,原因之一就是这时他们开始学习成年人的绘画技法,首先练习用线条勾勒出人脸。
在大多数领域,看上去容易的事情,背后都需要大量的练习。练习的作用也许是训练你把刻意为之的事情变成一种自觉的行为。有时,我们的训练只是为了让身体养成下意识的反应。优秀钢琴家弹奏名曲可以不经过大脑直接完成,艺术家也是这样,熟练以后,脑海中的艺术形象会自动从手上流淌出来,仿佛有人在一旁为他打节奏一样。
人们有时会说自己有了“状态”,我的理解是,他们这时可以控制自己的脊髓。脊髓是更本能的反应,面对难题时,它能释放你的直觉。
好设计是对称的设计。对称也许只是简洁性的一种表现,但是它十分重要,值得单独列为一点。自然界的对称大量存在,这就说明了对称的重要性。
对称有两种:重复性对称和递归性对称。递归性对称就是指子元素的重复,比如树叶上叶脉的纹路。
历史上,对称曾经泛滥一时,导致现在它在某些领域已经不流行了。从维多利亚女王时代开始,建筑师就有意多建造不对称的建筑。20世纪20年代,不对称成了现代主义建筑的一个明确的前提条件。但是即使如此,这些建筑物往往也只是在主轴上不对称,细节部分依然大量使用对称。
在写作中,你会发现对称无处不在,短语、句子、小说的情节都是如此。音乐和美术也大量使用对称。拼接式的美术作品(还有塞尚的一部分作品)有非常强烈的视觉感染力,原因就是整幅作品由相同的作图元素构成,这也属于对称。对称性构图产生了一些让人难忘的绘画作品,尤其是那些两个半边互相呼应的作品,比如米开朗基罗的壁画《创世纪》和格兰特·伍德的油画《美国式哥特》。
在数学和工程学中,递归尤其有用。归纳式证明方法既简洁又美妙。在软件中,能用递归解决的问题通常代表已经找到了解法。巴黎的埃菲尔铁塔如此引人注目,部分原因就是它的外形是递归的,大塔上面还有小塔(图9-2)。
图9-2 埃菲尔铁塔,1889。大塔上面有小塔
对称的危险在于它可以用来取代思考,在大量使用重复的时候这种危险性更大。
好设计是模仿大自然的设计。我不是说模仿大自然这种行为本身有多么好,而是说大自然在长期的演化中已经解决了很多设计问题。所以,如果你的设计与大自然很接近,那么它基本上不会很差。
模仿与剽窃并不相同。如果一部小说写得好像真实生活的再现,没人会提出异议。虽然写实的价值常常被误解,但它也是绘画的一个重要工具。写实的目的不是为了给生活留下一模一样的记录,而是为你的思想提供一个咀嚼点:你的眼睛看着某样东西,你的手就代表你的思想,画出一些比较有意思的内容。
模仿大自然也是工程学的有效方法。长久以来,船只就像动物一样有龙骨和肋骨。不过,前提条件是技术水平要达到,只有这样才有可能模仿大自然。早期的飞机设计师按照鸟的形状设计飞机,这样做其实是错的,因为那时还没有足以模拟鸟类行为的轻型材料和能源,也做不出高度复杂的控制系统,所以飞机还不可能像鸟类那样飞。但是,我能想象五十年后,小型的无人侦察飞机可以做得完全像鸟一样。
现在的计算机已经很强大了,不仅能模拟出大自然的环境,还能模拟大自然发展演变的结果。遗传算法可能会创造出正常条件下难以设计的复杂事物。
图9-3 达·芬奇,《一匹直立的马的研究》,1481—1499
好设计是一种再设计。很少有人一次就把事情做对。专家的做法是先完成一个早期原型,然后提出修改计划,后把早期原型扔掉。
扔掉早期原型是需要信心的,你必须有本事看出什么地方还可以改进。举例来说,刚刚开始学画的人往往不愿意重画画错的地方。他们觉得能画成现在这样已经很不错了,如果重画某些部分,结果可能还不如现在。所以,他们就说服自己,我的画已经过得去了,没准别人也会这么看。
这想法很危险。你应该培养对自己的不满。达·芬奇为了把一根线画对,经常要画五六次。保时捷911型汽车的原型很粗糙,只有在重新设计后它的背部轮廓才变成现在这样独特的曲线。建筑师莱特设计的古根海姆博物馆,早的时候,右半边有点像古代的塔庙(ziggurat),他后来把它倒过来,就成了现在的样子。
犯错误是很正常的事情。你不要把犯错看成灾难,要勇于承认、勇于改正。达·芬奇实际上重新发明了素描这种艺术形式,把它当作一种探索更多可能的方式。开源软件因为公开承认自己会有bug,反而使得代码的bug比较少。
做修改的时候,有一个合适的工具会使得改动更容易。美术史上,15世纪油彩取代蛋彩(tempera)就是一个重大突破,油彩使得画家更方便地处理那些困难的主题(比如人体),因为油彩可以调制,还可以重画,蛋彩就做不到这些。
好设计是能够复制的设计。我们对待复制的态度经常是一个否定之否定的过程。刚入门的新手不知不觉地模仿他人,逐渐熟练之后才开始创作原创性作品。后他会意识到,把事情做对比原创更重要。
不知不觉的模仿几乎必然将导致坏设计。如果你不知道自己的想法从何而来,那么你可能就是在模仿另一个模仿者。19世纪中期,拉斐尔画派主导了整个画坛,几乎每个学画的人都在模仿拉斐尔,可是经常谬以千里。有一些艺术家实在看不下去了,被如此之多模仿拉斐尔的人搞烦了,于是成立了前拉斐尔画派。
等到你逐渐对一件事产生热情的时候,就不会满足于模仿了。你的品味就进入了第二阶段,开始自觉地进行原创。
我想,伟大的大师终会达到一种超脱自我的境界。他们一心想找到正确答案,如果别人已经回答出了一部分,那就没理由不拿来用。他们足够自信地使用他人的成果,完全不担心因此丧失个人的特点。
好设计常常是奇特的设计。某些出色的作品堪称不可思议:欧拉公式、16世纪画家布勒哲尔的《雪中猎人》(图9-4)、SR-71“黑鸟”超音速侦察机(图9-5)、计算机的Lisp语言等。它们不仅优美,而且美得很奇特。
图9-4 布勒哲尔的《雪中猎人》,1565年
图9-5 洛克希德公司的SR-71“黑鸟”超音速侦察机,1964年
我不太确定原因,可能是因为我不够聪明,才会觉得它们看上去很奇特。一条狗看到开罐器也会认为那是一个奇迹。如果我是天才的话,可能会觉得是再平常不过的事情,它又没有说错,有什么好奇怪的。
我在前文提到的好设计的大多数特点都是可以培育出来的,但是我觉得“奇特”这个特点是无法培育的。你多就是在它开始显现时不要把它扼杀掉。爱因斯坦并不想让相对论变得很奇特,他只想找出真理,是真理本身显得很奇特。
我曾在一家美术学校学习绘画,那里的学生想做的就是发展出一种自己的风格。但是,如果你想做出好作品,不可避免地会采用一种独特的方式,就好像每个人走路的姿势其实都不尽相同。米开朗基罗并没想过要树立米开朗基罗风格,他只是想画好作品,结果不由自主地创造出了米开朗基罗风格。
你后发展出来的风格是自然而然形成的。“奇特”这个特点尤其如此,没有其他路可走。它就像连接大西洋和太平洋的“西北航道”,无数人希望找到这条捷径。16世纪的风格主义者、19世纪的浪漫主义者、一代代的美国高中生都在寻找,但就是找不到。达到“奇特”的方法,就是追求做出好作品,完成之后再回过头看。
好设计是成批出现的。15世纪住在佛罗伦萨的伟大艺术家有建筑师布鲁内莱斯基、画家吉贝尔蒂、雕塑家多纳泰洛、画家马萨乔、画家菲利普里皮、画家弗拉安吉利科、雕塑家韦罗基奥、画家波提切利、达·芬奇和米开朗基罗。当时,米兰也是同等的大城市,请问你能说出15世纪米兰城有什么伟大艺术家吗?
15世纪的佛罗伦萨有一些独特的条件,它们是不可延续的,因为今天的佛罗伦萨已经不是如此了。我们还必须假设达·芬奇和米开朗基罗拥有的天赋,在米兰城里一定也有人拥有。那么为什么没有出现米兰的达·芬奇呢?
今天,生活在美国的人口大概是15世纪佛罗伦萨的一千倍。那么按照比例推算,在我们之中存在着一千个达·芬奇和一千个米开朗基罗。如果这种推算成立,我们应该每天都看到令人惊叹的艺术奇迹。但是,事实并非如此,原因就是达·芬奇的出现除了他本身的天赋以外,还有赖于1450年的佛罗伦萨。
推动人才成批涌现的因素就是,让有天赋的人聚在一起,共同解决某个难题。互相激励比天赋更重要,达·芬奇之所以成为达·芬奇,主要原因不仅仅是他的天赋,更重要的是他生活在当时的佛罗伦萨,而不是米兰。今天,人类生活的流动性高得多,但是伟大的项目依然不成比例地集中在少数几个热点上:德国包豪斯建筑学院、曼哈顿计划、《纽约人》杂志、洛克希德公司的臭鼬工作室、施乐公司的帕洛阿尔托研究中心。
在历史的任何时刻都有一些热点项目,一些团体在这些项目上做出伟大的成绩。如果你远离这些中心,几乎不可能单靠自己就取得伟大成果。某种程度上,你个人多可以对趋势产生一定的影响,但是你不可能决定趋势,实际上是趋势决定了你。(或许有人办得到,但是米兰的达·芬奇显然没有办到。)
好设计常常是大胆的设计。在任何一段历史中,人们都会把某些荒谬的东西当作正确的,并且深信不疑,以至于一旦你出言质疑,就有被排挤或者被暴力伤害的危险。
我们自己的这个时代要是不同以往,当然令人欢欣鼓舞。但是就我所知,它并没有任何不同。
这个问题不仅存在于每个年代,还或多或少存在于每个领域。许多文艺复兴时期的艺术作品在当时都被认为极其大逆不道。根据意大利画家瓦萨里的记载,波提切利因此向教会忏悔并且放弃绘画,巴尔托洛梅奥和洛伦索迪克雷迪则是把自己的作品烧掉。爱因斯坦的相对论触犯了许多同时代的物理学家,许多年后还没有被完全接受,法国物理学家直到20世纪50年代才接受相对论。
今天的实验性错误就是明天的新理论。如果你想做出伟大的新成果,那就不能对常识与真理不相吻合之处视而不见,反而应该特别注意才对。
实际上,我觉得发现丑陋的东西要比你想象出一个优美的东西更容易。大多数做出优美成果的人好像只是为了修正他们眼中丑陋的东西。伟大成果的出现常常来源于某人看到一样东西后,心想我能做得比这更好。拜占庭帝国的《圣母像》早是根据某个公认的模板画的,非常机械呆板。几百年后的14世纪,意大利画家乔托看到以后,深感不满,决定动手改进,他因此成为文艺复兴的先行者。哥白尼对地心说无法解释的事情深感困扰,他的同时代人都觉得这可以忍受,他却认为一定能找到一种更好的解释。
单单是无法容忍丑陋的东西还不够,只有对这个领域非常熟悉,你才可能发现哪些地方可以动手改进。你必须锻炼自己。只有在成为某个领域的专家之后,你才会听到心里有一个细微的声音说:“这样解决太糟糕了!一定有更好的选择。”不要忽视这种声音,要培育它们。优秀作品的秘诀就是:非常严格的品味,再加上实现这种品味的能力。
一百年后的编程语言
很难预测一百年后的人类生活,只有少数几件事是可以确定的。那时,汽车将具备低空飞行能力,城市规划的法规将放宽,大楼可以造到几百层,大街上一天到晚看不见太阳,女性个个都学过防身术。本文只想讨论其中的一个细节:一百年后,人们使用什么语言开发软件?
为什么这个问题值得思考?原因不是我们终会用上这些语言,而是幸运的话,我们从现在开始就能用上这些语言。
我认为,编程语言就像生物物种一样,存在一个进化的脉络,许许多多分支终都会成为进化的死胡同。这种现象已经发生了。Cobol语言曾经流行一时,但是现在看来没有任何后续语言继承它的思想。它就像尼安德特人一样,进化之路已经走到了尽头。
我预言Java也会如此。有人写信说:“你怎么能说Java不会成功呢?它已经成功了。”我觉得这要看你的成功标准是什么。如果标准是相关书籍的出版量,或者是相信学会Java就能找到工作的大学生数量,那么Java确实已经成功了。当我说Java不会成功时,我的意思是它和Cobol一样,进化之路已经走到了尽头。
这只是我的猜测,未必正确。这里的重点不是看衰Java,而是提出编程语言存在一个进化的脉络,从而引导读者思考,在整个进化过程中,某一种语言的位置到底在哪里?之所以要问这个问题,不是为了一百年后让后人感叹我们曾经如此英明,而是为了找到进化的主干。它会启发我们去选择那些靠近主干的语言,这样对当前的编程有利。
无论何时,选择进化的主干可能都是方案。要是你不幸选错了,变成了一个尼安德特人,那就太糟了。你的对手克鲁马努人时不时就会来攻打你,把你的食物全部偷走。
这就是我想找出一百年后的编程语言的原因。我不愿意押错赌注。
编程语言的进化与生物学进化还是有区别的,因为不同分支的语言会发生聚合。比如,Fortran分支看来正在与Algol的继承者聚合。理论上,不同的生物物种也可能发生聚合,但是可能性很低,所以大概从来没有真正出现过。
编程语言之所以可能出现聚合,一个原因是它的概率空间比较小,另一个原因是它的突变不是随机的。语言的设计者们总是有意识地借鉴其他语言的设计思想。
对于语言设计者来说,认清编程语言的进化路径特别有用,因为这样就可以照着样子设计语言了。这时,认清进化的主干就不仅有助于识别现存的优秀语言,还可以把它当作设计语言的指南。
任何一种编程语言都可以分成两大组成部分:基本运算符的集合(扮演公理的角色)以及除运算符以外的其他部分(原则上,这个部分可以用基本运算符表达出来)。
我认为,基本运算符是一种语言能否长期存在的重要因素。其他因素都不是决定性的。这有点像买房子的时候你应该先考虑地理位置。别的地方将来出问题都有办法弥补,但是地理位置是没法变的。
慎重选择公理还不够,还必须控制它的规模。数学家总是觉得公理越少越好,我觉得他们说到了点子上。
你仔细审视一种语言的内核,考虑哪些部分可以被摒弃,这至少也是一种很有用的训练。在长期的职业生涯中,我发现冗余的代码会导致更多冗余的代码,不仅软件如此,而且像我这样性格懒散的人,我发现在床底下和房间的角落里这个命题也成立,一件垃圾会产生更多的垃圾。
我的判断是,那些内核小、干净的编程语言才会存在于进化的主干上。一种语言的内核设计得越小、越干净,它的生命力就越顽强。
当然,猜测一百年后人们使用什么编程语言,这本身就是一个很大的假设。也许一百年后人类已经不编程了,或者直接告诉计算机想做什么,计算机就会自动完成。
不过,到目前为止,计算机智能并没有取得太大进展。我猜测一百年后,人们还是使用与现在差不多的程序指挥计算机。可能有一些我们今天需要编程解决的问题,那时已经不需要编程了,但是我想,那时还会存在大量与今天一样的编程任务。
你可能认为只有那些自以为是的人才会去预言一百年后的技术。但是,请不要忘记,软件发展的历史已经走过了50年。在这50年中,编程语言的进化其实是非常缓慢的,因此展望一百年后的语言并不是虚无缥缈的想法。
编程语言进化缓慢的原因在于它们并不是真正的技术。语言只是一种书写法,而程序则是一种严格符合规则的描述,以书面形式记录计算机应该如何解决你的问题。所以,编程语言的进化速度更像数学符号的进化速度,而不像真正的技术(比如交通或通信技术)的进化速度。数学符号的进化是缓慢的渐变式变化,而不是真正技术的那种跳跃式发展。
无论一百年后的计算机是什么样子,我们基本上可以断定它们的运行速度一定会快得多。如果摩尔定律依然成立,一百年后计算机的运行速度将是现在的74乘以10的18次方倍(准确地说是73786 976 294 838 206464倍)。真是让人难以想象。不过实际上更现实的预测并不是速度会提高这么多,而是摩尔定律终将不成立。不管是什么东西,如果每18个月就增长一倍,那么后很可能会达到极限。但那时的计算机比现在快得多大概是毫无疑问的。即使后只是略微快了100万倍,也将实质性地改变编程的基本规则。如果其他条件不变,现在被认为运行速度慢的语言(即运行的效率不高)将来会有更大的发展空间。
那时,依然会有对运行速度要求很高的应用程序。我们希望计算机解决的有些问题其实是计算机本身引起的。比如,计算机处理视频的速度取决于生成这些视频的另一台计算机。此外,还有一些问题本身就要求无限快的处理能力,比如图像渲染、加密/解密、模拟运算等。
既然在现实中一些应用程序本身的效率较低,而另一些应用程序会耗尽硬件提供的所有运算能力,那么有了更快速的计算机就意味着编程语言不得不应付更多的情况,涵盖更大范围的效率要求。我们已经看到这种情况发生了。要是以几十年前的标准衡量,有一些使用新语言开发的热门应用程序对硬件资源的浪费非常惊人。
不仅编程语言有这种现象,这实际上是一种普遍的历史趋势。随着技术的发展,每一代人都在做上一代人觉得很浪费的事情。30年前的人要是看到我们今天如此随意地使用长途电话,一定会感到震惊。100年前的人要是看到一个普通的包裹竟然也能享受一天内从波士顿发件、途经孟菲斯、抵达纽约的待遇,恐怕就要更震惊了。
我已经预测了,一旦未来硬件的性能大幅提高将会发生什么事。新增加的运算能力都会被糟蹋掉。
在我学习编程的年代,计算机还是稀罕玩意。我记得当时使用的微机型号是TRS-80,它的内存只有4K,为了把BASIC程序装入内存,我不得不把源码中的空格全部删除。我一想到那些极其低效率的软件,不断重复某些愚蠢的运算,把硬件的计算能力全部占用,就感到无法忍受。但是,我的这种反应是错的,我就像某个出身贫寒的穷孩子,一听到要花钱就舍不得,即使把钱用在重要场合(比如去医院看病)都觉得很难接受。
某些浪费确实令人厌恶。比如有人就很讨厌SUV(运动型多用途车),即使它采用可再生的清洁能源也改变不了看法,因为SUV来自一个令人厌恶的想法(如何使得小货车看上去更有男子汉气概)。但是,并非所有的浪费都是坏的。既然如今的电信基础设施已经如此发达,再掐着时间打长途电话就有点锱铢必较了。如果有足够的资源,你可以将长途电话和本地电话视为同一件事,一切会变得更轻松。
浪费可以分成好的浪费和坏的浪费。我感兴趣的是好的浪费,即用更多的钱得到更简单的设计。所以,问题就变成了如何才能充分利用新硬件更强大的性能有利地“浪费”它们?
对速度的追求是人类内心深处根深蒂固的欲望。当你看着计算机这个小玩意,就会不由自主地希望程序运行得越快越好,真的要下一番功夫才能把这种欲望克制住。设计编程语言的时候,我们应该有意识地问自己,什么时候可以放弃一些性能,换来一点点便利性的提高。
很多数据结构存在的原因都与计算机的速度有关。比如,今天的许多语言都同时有字符串和列表。从语义上看,字符串或多或少可以理解成列表的一个子集,其中的每一个元素都是字符。那么,为什么还需要把字符串单列为一种数据类型呢?完全可以不这么做。只是为了提高效率,所以字符串才会存在。但是,这种以加快运行速度为目的、却使得编程语言的语义大大复杂的行为,很不可取。编程语言设置字符串似乎就是一个过早优化的例子。
如果我们把一种语言的内核设想为一些基本公理的集合,那么仅仅为了提高效率就往内核添加多余的公理,却没有带来表达能力的提升,这肯定是一件很糟的事。没错,效率是很重要,但是我认为修改语言设计并不是提高效率的正确方法。
正确做法应该是将语言的语义与语言的实现予以分离。在语义上不需要同时存在列表和字符串,单单列表就够了。而在实现上做好编译器优化,使它在必要时把字符串作为连续字节的形式处理。
对于大多数程序,速度不是关键的因素,所以你通常不需要费心考虑这种硬件层面上的微观管理。随着计算机速度越来越快,这一点已经越发明显了。
语言设计时,对实现方式少作限制还会使得程序具备更大的灵活性。语言的规格发生变化不仅是无法避免的,也是合理的。通过编译器的处理,按照以前规格开发的软件就会照常运行,这就提供了灵活性。
essay(论文)这个词来自法语的动词essayer,意思是“试试看”。从这个原始意义来说,论文就是你写一篇文章,试着搞清楚某件事。软件也是如此。我觉得一些好的软件就像论文一样,也就是说,当作者真正开始动手写这些软件的时候,他们其实不知道后会写出什么结果。
Lisp语言的黑客早就明白数据结构灵活性的价值。我们写程序的版时,往往会把所有事情都用列表的形式处理。所以,这些初版本可能效率低下得惊人,你必须努力克制自己才能忍住不动手优化它们,这就好像吃牛排的时候,必须努力克制自己才能不去想牛排是从哪里来的一样,至少对我来说是这样的。
一百年后的程序员需要的编程语言就是可以让你毫不费力地写出程序版的编程语言,哪怕它的效率低下得惊人(至少按我们今天的眼光来看是如此)。他们会说,他们想要的就是很容易上手的编程语言。
效率低下的软件并不等于很烂的软件。一种让程序员做无用功的语言才真正称得上很烂。浪费程序员的时间而不是浪费机器的时间才是真正的无效率。随着计算机速度越来越快,这会变得越来越明显。
我觉得,放弃字符串类型已经是大家可以接受的想法了。Arc语言已经这样做了,看上去效果不错。以前用正则表达式很难描述的一些操作,现在用回归函数可以表达得很简单。
这种数据结构的扁平化趋势会怎么发展?我极其努力地设想各种可能,得到的结果甚至令我自己都吓了一跳。比如,数组会不会消失?毕竟数组只是散列表的一个子集,其特点就是数组的键全部都是整数向量。进一步说,散列表本身会不会被列表取代呢?
还有比这更惊人的预言。在逻辑上其实不需要对整数设置单独的表示法,因为可以把它们也看作列表,整数n可以用一个n元素的列表表示。这一样能完成数学运算,只是效率低得让人无法忍受。
编程语言会发展到放弃基本数据类型之一的整数这一步吗?我这样问并不是真的要你严肃思考这个问题,更多的是希望打开你对未来的思路。我只是提出一种假想的情况:如果一股不可抗拒的力量遇到了一个不可移动的物体,会发生什么事。具体就本文而言:一种效率低得不可想象的语言遇到了性能强大得不可想象的硬件,会发生什么事。我看不出放弃整数类型有什么不妥。未来相当漫长。如果我们想要减少语言内核中基本公理的数目,不妨把眼光放得远一点,想一想如果时间变量t趋向无限会怎么样。一百年是一个很好的参考指标,如果你觉得某个想法在一百年后仍然可能是难以令人接受,那么也许一千年后它也依然难以令人接受。
让我说清楚,我的意思不是说所有的整数运算都用列表来实现,而是说语言的内核(不涉及任何编译器的实现)可以这样定义。在现实中,任何进行数学运算的程序可能都是以二进制形式表示数字,但是这属于编译器的优化,而不属于语言内核语义的一部分。
另一种消耗硬件性能的方法就是,在应用软件与硬件之间设置很多的软件层。这也是我们已经看到的一种趋势,许多新兴的语言就被编译成字节码。比尔·伍兹曾经对我说,根据经验判断,每增加一个解释层,软件的运行速度就会慢一个数量级。但是,多余的软件层可以让编程灵活起来。
Arc语言初的版本就是一个的例子,它的层很多,运行速度非常慢,但是确实带来了相应的好处。Arc是一个典型的“元循环”(metacircular)解释器,在CommonLisp的基础上开发,很像约翰·麦卡锡在他经典的Lisp论文中定义的eval函数。Arc解释器一共只有几百行代码,所以很便于理解和修改。我们采用的CommonLisp版本是CLisp,它本身是在另一个字节码解释器的基础上开发的。所以,我们一共有两层解释器,上面那层效率低下得惊人,但是语言本身是能用的。我承认只是勉强可用,但是确实能用。
即使是应用程序,使用多层形式开发也是一种很强大的技巧。自下而上的编程方法意味着要把软件分成好几层,每一层都可以充当它上面那一层的开发语言。这种方法往往会产生更小、更灵活的程序。它也是通往软件圣杯——可重用性(reusability)——的路线。从定义上看,语言就是可以重用的。在编程语言的帮助下,你的应用程序越是采用这种多层形式开发,它的可重用性就越好。
可重用性这个概念多多少少与20世纪80年代兴起的面向对象编程有些关联。不管怎样寻找证据,也不可能把这两件事完全分开。某些使用面向对象编程开发出来的软件确实具有可重用性,但是这不是因为它使用了面向对象编程,而是因为它的开发方法是自下而上的。以函数库为例,它们具有可重用性,是因为它们属于语言的一部分,而不是因为它们采用面向对象或者其他编程方法。
顺便说一句,我不认为面向对象编程将来会消亡。我觉得,除了某些特定的领域,这种编程方法其实没有为优秀程序员带来很多好处,但是它对大公司有不可抗拒的吸引力。面向对象编程使得你有办法对一团乱码似的代码进行可持续性开发。通过不断地打补丁,它让你将软件一步步做大。大公司总是倾向于采用这样的方式开发软件。我预计一百年后也是如此。
既然是谈论未来,好谈谈并行计算(parallelcomputation),因为看上去并行计算好像就是为未来而存在的。无论怎么想,并行计算似乎都是未来生活的一部分。
它会在未来实现吗?过去二十年,人们都在说并行计算马上就会来临。但是,到目前为止,它对编程实践并没有太大影响。这是真的吗?芯片设计师已经不得不把它考虑在内,为多CPU计算机开发系统软件的程序员也是如此。
但是,真正的问题在于,并行计算到底能达到哪个抽象层次?一百年后它就会影响到开发应用软件的程序员吗?或者,它还只是编译器作者需要考虑的事情,在应用软件的代码中根本就无处寻觅?
一种可能是,大多数可以用到并行计算的场合,人们都会放弃使用并行计算。虽然我总的预测是未来的软件会挥霍掉大部分新增的硬件性能,但是并行计算是一个特例。我估计随着硬件性能得到惊人的提升,如果你明确地说想要并行计算,那么肯定可以得到它,但是通常情况下你不会用到它。这意味着,除了一些特殊的应用程序,一百年后的并行计算不会是那种大规模的并行计算(massiveparallelism)。我预料,对于普通程序员来说,一切更像对进程进行分叉,然后让多个进程在后台并行运行。
这是编程进行到很后期才要做的事情,属于对程序的优化,类似于你想开发一种特定的数据结构来取代现有的数据结构。程序的个版本通常会忽略并行计算提供的各种好处,就好像编程开始时会忽略某种特定的数据结构给你带来的好处一样。
除了某些特定的应用软件,一百年后,并行计算不会很流行。如果应用软件真的大量使用并行计算,这就属于过早优化了。
一百年后会有多少种编程语言?从近来看,出现了大量的新语言。硬件性能提高是一个原因,这就允许程序员根据使用目的在运行速度和编程便利性之间做出不同的取舍。如果这就是未来的趋势,那么一百年后强大的硬件只会使得语言数目变得更多。
但是,另一方面,一百年后的常用语言可能只有很少几种。部分原因是基于我的乐观主义,我相信在未来,如果你的作品确实很出色,你可能选择的是一种开发起来很方便的语言。使用这种语言写出来的软件版的运行速度很慢,只有对编译器进行优化设置后运行速度才会提升。既然我抱有这种乐观主义,那么我还要做一个预言。有些语言可以达到机器的效率,另一些语言的效率则慢到刚刚可以运行而已,两者之间存在巨大的差距。我预言一百年后,这段差距之间的各个点上都会有对应的编程语言存在。
因为这段差距正在变得越来越大,所以性能分析器(profiler)将变得越来越重要。目前,性能分析并没有受到重视。许多人好像仍然相信,程序运行速度提升的关键在于开发出能够生成更快速代码的编译器。代码效率与机器性能的差距正在不断加大,我们将会越来越清楚地看到,应用软件运行速度提升的关键在于有一个好的性能分析器帮助指导程序开发。
我说将来可能只有很少几种常用语言,但没有把用于特定领域的“小众语言”(littlelanguage)算进去。我觉得,这些嵌入式语言的想法很不错,一定会蓬勃发展。但是我判断这些“小众语言”会被设计成相当薄的一层,使得用户可以一眼看出在底下作为基础的通用型语言,这样就减少了学习时间,降低了使用成本。
谁来设计这些未来的语言?过去10年激动人心的趋势之一就是开源语言的崛起,比如Perl、Python和Ruby。语言设计已经被黑客接管。到目前为止这样到底是好是坏还看不清楚,但是发展势头令人鼓舞。比如,Perl就有一些绝妙的创新。不过,它也包含了一些很糟糕的想法。对于一种充满进取心、大胆探索的语言来说,这也是很正常的事。以它现在这种变化的速率,大概只有上帝才知道一百年后Perl会变成什么样。
有一句俗话说,如果你自己做不到,那就去当老师。这在语言设计领域不成立,我认识的一些出色的黑客就在当教授。但是,当老师的人确实有很多事情不能做。研究性职位给黑客带来了一些限制。在任何学术领域,都有一些题目是可以做的,另一些题目是不可以做的。不幸的是,这两类题目的区别通常取决于它们写成论文后看上去是不是很高深,而不是取决于它们对软件业的发展是否重要。的例子可能就是文学,文学研究者的任何成果几乎对文学创作者都毫无影响。
虽然科学领域的状况要稍好一点,但是研究者可以做的题目与能够对设计优秀语言有所帮助的题目之间的交集小得令人沮丧。(奥林·希弗斯曾经对这一点表达不满,而且说得头头是道。)比如,研究变量类型的论文好像多得无穷无尽,尽管事实上静态类型语言看来无法真正支持宏(在我看来,一种语言不支持宏,那就不值得使用了)。
新语言更多地以开源项目的形式出现,而不是以研究性项目的形式出现。这是语言的一种发展趋势。另一种发展趋势是,新语言的设计者更多的是本身就需要使用它们的应用软件作者,而不是编译器作者。这似乎是好的趋势,我期待它继续保持下去。
一百年后的物理学基本上不可能预测。但是计算机语言不一样,现在就动手设计一种一百年后可以吸引使用者的新语言,这在理论上似乎是可能的。
设计新语言的方法之一就是直接写下你想写的程序,不管编译器是否存在,也不管有没有支持它的硬件。这就是假设存在无限的资源供你支配。不管是今天还是一百年后,这样的假设好像都是有道理的。
你应该写什么程序?随便什么,只要能让你省力地写出来就行。但是要注意,这必须是在你的思维没有被当前使用的编程语言影响的情况下。这种影响无处不在,必须很努力才能克服。你也许觉得,对于人类这样懒惰的生物,喜欢用省力的方式写程序是再自然不过的事情。但是事实上,我们的思想可能往往会受限于某种现存的语言,只采用在这种语言看来更简单的形式,它对我们思想的束缚作用会大得令人震惊。新语言必须靠你自己去发现,不能依靠那些让你自然而然就沉下去的思维定势。
采用程序的长度作为它耗费工作量的近似指标是个很有用的技巧。这里的程序长度当然不是指字符的数量,而是指各种句法元素的总长度,基本上就是整个解析树的大小。也许不能说短的程序就是写起来省力的程序,但是当你一心想把程序写得简洁而不是松松垮垮时,你就更接近省力这个目标,你的日子也会变得好过得多。所以,设计语言的正确做法就变成了,看着一段程序,然后问自己是不是能把它写得更短一点?
……
评论
还没有评论。