Paul Graham 2003年4月程序员
(本文出自2003年Python大会上的一篇主题讲话)正则表达式
很难预测人们的生活在一百年后会是什么样子,咱们只能给不多的事物一个确切的预测。咱们知道到那时候每一个人都将驾驶气垫轿车,地方法规将对建造上百层的高楼无所制约,大部分时间都将日月无光,女人们都将精通武术(martial arts)……在这里,让咱们把这幅图景的一个细节放大来看看:那时候人们用什么编程语言来写那些气垫轿车的控制软件呢?编程
这是一个值得思考的问题,其意义不在于咱们必定要用这种语言,而是在于据此咱们能够选择可能发展成那种语言的语言——若是咱们够幸运的话。数组
我认为,语言就像物种同样,会造成进化树,没有前途的分支将枯死脱落。咱们已经看到了这种事情发生:Cobol——曾几什么时候风光无限,现现在没有一个像样的后代,它就是在进化中被淘汰的“穴居人”语言。(译注:穴居人是石器时代的欧洲大陆的主宰,大约3万年前灭绝。穴居人又叫尼安德特人,其发声系统不发达。)网络
我预测Java的气数也跟这种语言差很少。有人不时发邮件跟我说:“你怎么能说Java不可能成为一种成功的语言呢?它如今已是一种至关成功的语言了。”那么我认可这一点——若是你衡量成功的标准是关于Java的书籍(特别是我的著做)在书架上占去的空间的大小,或者是为了找工做不得不学习Java的大学生的数量的多少的话。我所说Java不可能成为一种成功的语言,意思是从物种进化的角度来看,Java将会走向穷途末路,就像 Cobol同样。数据结构
这只是一个猜测,我也许会猜错。我在此的重点不是要讨论Java,而是要提出进化树的论点并引起人们来问本身:“X语言在进化树上的什么位置?”之因此提出这个问题,不只为了不百年后去后悔,更主要是由于跟紧语言发展的主流对于当前选择好的编程语言有积极的启发意义。编程语言
假如你生活在旧石器时代,任什么时候候你大概都会由于本身“处在进化树的主干上”(译注:石器时代地球上生活着智人在内的多我的种,后来其余人种都灭绝了,只有智人在竞争中生存下来,成为现代人类的祖先)而感到无比幸福,虽然还有大量的穴居人——他们也是这个世界的居民,而且克鲁马努人(译注:旧石器时代晚期在欧洲的高加索人种)不时会来袭击你,还偷走你的食物。函数
所以我也想知道编程语言在一百年之后会是什么样子,从而决定如今该把赌注押在哪一个“树枝”上。性能
编程语言的进化过程又不一样于物种的进化过程,由于编程语言的分支可能会汇聚。譬如Fortran这个分支,彷佛正在渐渐并入到Algol的后代中。理论上讲这对于物种来讲也是可能的,可是这种可能性很小,彷佛历来就没有发生过。学习
集中化对于语言的进化更有可能,部分缘由是语言进化的走向空间比较小,还有部分缘由是对语言的进化来讲,突变不是随机的。语言的设计者总会有意识地吸收其余语言的思想。
对于语言设计者来讲,考虑一下编程语言的进化方向就特别有意义,由于他们能够据此把握好设计取向。在那种状况下,“处在进化树的主干上”就不仅是选择一个好的语言了,而是从中获得启发,以对语言的设计作出正确的决策。
任何编程语言均可以分为两个部分:做为公理(axiom)的一个基本运算符(operator)集和语言的其余部分,其余部分原则上能够根据基本语素写出来。我想基本语素集是一种语言在其漫长的生存期中最重要的部分了,而其余部分可能会改变。这就比如买一幢房子,原则上你应该首先考虑房子的地理位置,其余的任何因素你均可以调整,可是你不能调整位置。
我认为好的公理的选择很重要,可是公理要尽可能少,这一点一样重要。数学家们对于这一点感觉应该更深入:公理越少越好。我认为也确实如此。
最近,人们仔细核查起编程语言的核心,看看是否是有什么“公理”是能够除去的,这已经成为一项有益的实践。我发如今我长期的职业生涯里,本身常常像个笨蛋同样,用垃圾堆积着垃圾(译注:原文cruft breeds cruft,随着软件的发展,以及经历了修改bug和更新的若干周期,它的部分代码已再也不使用但仍然保留在源码中。这种代码称为cruft。 cruf多是一两行无用代码或整个源文件模块。因为很难识别cruft,去除cruft 每每很困难。)而且我发现一样的事情在软件里随时随地都在发生。
我有一个预感,软件进化树的主干会贯穿于某些编程语言中,这些语言有着最小、最干净的“核”。一种语言越能用它本身来写本身,就越好。
固然,在提出一百年后编程语言会是什么样子的问题的时候,我作了一个很大的假设。一百年后咱们还写程序吗?咱们不是只须要告诉计算机咱们但愿他们作什么就能够了吗?到如今为止,这方面尚未大的进展。我想此后的一百年里,人们仍是要经过如今这样手工编写的程序来告诉计算机去作什么。或许有的问题咱们如今须要写程序来解决,而一百年后这些问题没必要再写程序来解决,可是我想咱们仍是要面对不少咱们今天编程所面临的一样的问题。
谁要说他能预测某一技术在一百年后将是什么样子,咱们都会以为他在吹牛。可是不要忘记咱们已经有了五十年的经验,当咱们反思过去的五十年里语言的进化是多么缓慢的时候,再来展望一下一百年后的状况就是一件能够理解的事。
语言进化缓慢,是由于它们并非技术,语言只是符号。程序只是告诉计算机你要解决的问题的形式化的描述。编程语言的进化的速度并不像搬运或传递,倒更像数学符号的进化速度——数学符号也在进化,可是如你所见,却不像它所支持的技术同样有巨大而飞快的变化。
不管一百年后计算机是什么材料作的,能够很确定地预测它将比如今运行更快。若是摩尔定律(Moor’s Law)继续有效的话,它的速度将是如今的7,379亿亿(quintillion)(73,786,976,294,838,206,464)倍,这是不可思议的。不能否认,对于速度的预测摩尔定律极可能失效,任何事物若是每18个月就增加一倍的话,长到最后就极可能违背某些基本的极限。可是这不妨碍咱们去相信计算机会比如今快得多,即便它最后只比如今快那么小小的一百万倍,也会从本质上改变编程语言的基本规则。到那时候,那些当前被认为是运行速度缓慢、不能生成高效率生成码的语言就会获得更多的空间。
当然有一些应用还将追求速度。由于咱们用计算机解决的一些问题自己是由计算机引发的,好比你要处理的视频图像的速率依赖于另外一台产生视频图像的速率。另外还存在一些能够无限吃掉机时的问题,例如图像描述、加密、仿真等。
若是一些应用逐渐下降对效率的要求,而另外一些应用继续要求占用最新的硬件能提供的全部速度。那么更快的计算机就意味着语言必须覆盖一个更普遍的效率范围。咱们已经看到了这种事的发生,一些用新近流行的语言来实现的程序若是用几十年前的标准来衡量的话,那对机时的“浪费”是惊人的。
这不仅是发生在编程语言上的一个现象,而是一个广泛的历史趋势。当技术更新换代了之后,后一代都能作前一代会认为是浪费的事情。三十年前的人确定会以为咱们为所欲为地打长途电话是件使人惊讶的事,一百年前的人们必定会更惊讶于从波士顿通过孟菲斯到达纽约的包裹一天就能送到。
我如今就能够告诉你,一百年后当咱们有了更快的硬件之后那些新增的处理能力都作了些什么?它们都将被“浪费”掉!
我从计算机能力还很珍贵的时候开始学习编程。我还记得那时候从个人Basic程序中节省出全部能节省的空间以便装入一个4K大小的TRS-80,在这个无止境的重复上我花费了很大的精力,把机器的能力发挥到极限,最终仍是受不了这种低效。可是如今看来,我那时拼命节约机器资源的直觉是错误的——就如同一个从小受过贫穷的煎熬的人,连去看医生这样很重要的事情也不舍得花钱。
某些浪费当然是可耻的,譬如SUVs(译注:Sport Utility Vehicle,运动型多功能轿车)就被证实是一种拙劣的产品,即便它载油量很大且不会产生污染。SUV之因此拙劣,是由于它为了解决一个拙劣的问题—— 怎么让一辆小型货车看上去更威猛。不过不是全部的浪费都是坏的。咱们有证据来支持这一点,打长途电话的时候你不会繁琐地一分钟一分钟地数时间,若是有足够的资源,不管是打长途仍是短途,你可能会以为都是同样的。
有好的浪费,也有很差的浪费。我对好的浪费感兴趣,就是那种花更多的开销,可是能得到更简单的设计。咱们如何从浪费更新、更快的硬件的机时中获取好处呢?
在这个计算机处理能力很弱的时代,对速度的渴求在咱们心中早已根深蒂固,咱们应该有意识地克服这种想法。在语言设计中,咱们应该有意识地寻找一切机会,用执行效率来换取哪怕很小的使用便利性。
大多数数据结构都是因为速度的缘由而存在。譬如,当今不少语言都既有字符串又有列表(list)。从语义上讲,字符串是列表的一个子集——其元素为字符,所以你为何须要一种单独的数据类型呢?真的不须要。字符串的存在,只是由于效率的缘由。可是这种用扰乱语言语义的手法来使程序运行更快的作法是没有说服力的。语言中包含字符串类型就是一种不成熟的优化。
若是咱们把语言的核心当作是一组公理集,那么只简单地为了效率的好处而往这个公理集里面添加公理,却不能增长语言的表达力的话,这种添加就是丑陋的。效率是很重要,可是我不认为那种获取效率的方法是正确的。
我认为解决问题的正确方法是将程序的内涵与它的实现细节分离。不要既有列表又有字符串,只要有列表就够了,若是有必要,能够经过某种方式给编译器以建议,容许编译器把字符串按照相邻字节来存放。
既然速度在程序的绝大部分地方都可有可无,那么你一般就没必要为这种小事操心了,这一点随着计算机速度愈来愈快,将愈来愈正确。
人们不多注意到程序的实现也应当使程序变得更有弹性。一个程序每每在编写的过程当中会发生需求变化,这是不可避免的,也是应当受到欢迎的。
“essay”(译注:企图;小品文)一词源于法语的一个动词“essayer”,意思是“尝试”,也指为了“试图”推演出某一结论而写的东西。软件也跟essay同样,做者每每并不确知哪些才是他们真正要表达的东西,我认为一些最好的程序也是essay。
Lisp (译注:一种表处理语言,用于处理包含有表格的数据的编程语言,被普遍地运用于人工智能研究)黑客(hacker,译注:黑客指掌握尖端电脑技术的人,而不是人们常说的网络入侵者,下同)们已经了解到弹性数据结构的价值,咱们倾向于在程序的第一个版本中用列表(list)来处理一切数据。这种最初的版本是如此惊人地低效,由于它有意地避免去想它到底要作什么的细节,就像——至少对我来讲——是吃牛排的时候有意避免去想它来自哪里同样。
一百年后程序员将须要什么语言?最多是那种只须要最少的精力就写出 “很是低效”的“初版程序”就搞定问题的语言。至少,咱们目前能够如此描述这种语言。而用他们的话说,他们须要一种容易编程的语言。
低效的软件不是拙劣的,真正拙劣的是使程序员作没必要要的工做的语言。浪费机器时间不是低效,浪费程序员的时间才是真正的低效。这个道理随着计算机愈来愈快,也将会愈来愈明白。
我想去掉字符串(string)这种数据结构已经指日可待了。咱们在Arc(译注:Lisp的一种方言)中就已是这么作的,并且成功了。一些用正则表达式描述起来至关拙劣的操做,在Arc中用递归函数就很容易描述了。
像这种扁平的数据结构还能存在多久?我审慎而全面地思考了各类可能性,结果连我都大吃一惊。譬如,咱们将会放弃数组吗?毕竟,数组只是一种以整数向量为键值的哈希表(hash table),而咱们会连哈希表都用列表来取代吗?
有的预测甚至比这个更骇人听闻。譬如McCarthy在1960就描述过的Lisp语言中连数字(number)都没有。逻辑上讲,你并不必定须要一个关于数字的单独的符号,由于你能够用列表来表示数字,整数n能够用一个n个元素的列表来表示,你能够用这种方式进行数学计算。只不过这样不堪其低效。
现实中没有人被建议用列表来实现数字操做,事实上McCarthy在1960年的论文在那时也根本没有期望被实现。那只是一项理论探讨,只是尝试给图灵机(Turing Machine)创造一种优雅的替代。出人意料的是,有人却根据那篇文章作出了一个可工做的Lisp解释器。不过数字不是用列表来表示,而是跟其余语言里同样,用二进制的方式来表示的。
编程语言会发展到去掉数字这种基本数据类型的程度吗?我这么问不是信口开河地制造耸人听闻的将来问题。状况就如同无坚不摧的矛遇到了无所不克的盾——在这里是无比低效的代码遇到了无比丰富的硬件资源。我认为这彻底有可能,由于将来还至关长。若是某种作法能够减小语言的核心公理的数目,那么它随着时光飞逝会愈来愈值得“下注”。若是某种想法在一百年后依然是荒唐的,在一千年后或许未必荒唐。
必须声明,我并非建议全部的数值计算通通用列表来实现。我只是建议语言在没有为应用而增长任何额外的符号以前的核心部分这样来定义。实际上任何须要数学计算的程序大多会用二进制来表示数字,可是这只是一个优化,并在语言的核心语义范围中。
另外应用程序和硬件之间软件的多层次性也消耗了不少计算机机时。这也是咱们已经看到的发展趋势:许多软件只是被编译成字节码,运行在字节码解释器上。Bill Woods曾经告诉我,每一层解释器大概最起码要消耗10%的速度,这些额外的开销换来的是你程序的弹性。
Arc 的最第一版本就是这种软件分多层以获取弹性的极端的例子。那是一个创建在Common Lisp上的经典的“元循环”(metacircular)解释器,与McCarthy最初的Lisp论文中的eval函数(eval function)有必定的共同性。整个Arc只有200余行代码,因此很是易于理解和修改。而咱们所用的Common Lisp,即CLisp自己又运行在一个字节码解释器上。所以这里就存在两层解释器,其中一层(上面的那一层)是惊人的低效,可是Arc仍然能用。虽然我认可它用起来很勉强,可是毕竟能用。
在应用程序方面,把软件分层是一项了不起的技术。自底向上编程意味着一层一层地写程序,每一层做为 “语言”供上一层使用。这种方式有利于生成小而灵活的程序,也是取得可复用性这座“圣杯”(holy grail)的最佳途径——“语言”定义上的可复用性。在你写某种类型的应用程序的时候,越是能把你的应用下压到“语言”中,你的软件的可复用性也就越高。
不知何故可复用性的思想在1980年代被牵扯到面向对象编程中,而且看来尚未要把它解放出来的反对性的迹象。然而虽然某些面向对象软件是可复用的,可是使程序可复用的并非面向对象,而是它的自底向上特性。想一想库函数:它们可复用使由于它们是“语言”,不管它们是否是采用了面向对象的方法。
顺便说一句,我并非预测面向对象编程的灭亡。虽然我认为它不能给好的程序员更多的帮助,可是在某些专门的领域,一些大型组织仍是离不开它。面向对象编程提供了一种可行的方式去得到意大利面条式的代码,它容许你把一系列代码碎片拼合成程序。大型组织一般愿意用这种方式开发软件,我认为一百年后状况也仍是如此。
在咱们谈论将来的时候,最好也谈谈并行计算,由于那时候这种想法将成为现实。也就是说,并行计算看来必定会实现,不管你说那是何时。
将来能遇上并行计算吗?人们近20年来都在说并行计算即将实现,但是它到现在也尚未对编程产生实质影响。真的没有影响吗?芯片设计者们不得不考虑它,试图写在多CPU电脑上的系统软件的人们也不得不考虑它。
真正的问题在于,并行化会在抽象的道路上走多远?一百年后它会影响到应用软件的程序员吗?仍是只是编译器编写者们才考虑它,而通常应用软件的源代码里看不见其踪迹?
颇有可能的却是大多数并行化的机会都会被浪费。这是我所做的关于咱们所得到大多数额外的计算能力将会被浪费的预测的一个特例。我认为,随着未来硬件处理速度的巨大提高,并行化将不会很经常使用,除非你确实须要它。这代表咱们一百年后的并行化(除了特定的应用之外)都不会是大规模的并行化。我认为对普通程序员来讲,更多是生成一些并行运行的子进程。
这就像在程序生命的晚期你改变一个数据结构的精确实现同样,你只是在试图优化它。“初版程序”一般会忽略并行计算带来的好书,就像忽略数据的精确表述带来的好处同样。
所以,除了某些特定的应用软件之外,一百年内并行化将不会广泛在程序里使用。若是用了,将会是一种不成熟的优化。
一百年后将还存在多少种编程语言?最近彷佛有大量新的编程语言出现。部分缘由是更快的硬件容许程序员们在速度和便利性之间根据应用作出不一样的折中。若是语言的增可能是一个真实的趋势,那么一百年后咱们拥有的硬件只会增加这种趋势。
不过也许一百年后只剩下几种普遍使用的语言。我这样说,部分缘由是个人乐观:若是你真的干得好,你可能设计出某种语言,它既适合写出 “慢而便利”的“初版程序”,也能够在须要的时候经过给编译器一些适当的优化建议,让它产生很是快的生成码。由于个人乐观,我还能够预测无论“可接受” 的效率与“最高效”的效率之间的 “鸿沟”有多宽,一百年后程序员都将拥有合适的语言跨越它们。
随着“鸿沟”的变宽,性能评测器(profiler)将会愈来愈重要。如今软件性能评测方面关注的太少了,许多人都还相信编写出能产生快的生成码的编译器是让程序运行更快的途径。随着 “可接受性能”与“最优性能”之间的“鸿沟”的变宽,人们也愈来愈清楚得到更快的应用软件的正确途径是要有一个从“可接受”到“最优”的指引,就是性能评测器。
当我说将来只会剩余几种语言的时候,并不包括那些特定领域的“小语言”。我认为这些嵌入式语言是伟大的,我也但愿它们可以愈来愈多。可是我但愿它们写出来的东西可以像薄薄的外皮同样,让用户可以看到下面更通用的语言。
谁来设计将来的语言呢?过去十年里一个最振奋人心的趋势是Perl、Python、Ruby等开放源码语言兴起,黑客们接过了语言设计的任务。迄今为止结果虽然凌乱,但还算可嘉。例如,Perl里面就有一些极好的新颖的想法。虽然也有许多还很不足的地方,可是你们都在执着地努力。若是保持如今的转变速度,天知道一百年后Perl会进化成一个什么样的语言。
通常来说可实践者也是传授者(我认识一些最优秀的黑客都是教授),可是有许多领域传授者却不是实践者,所谓“学术”一贯以堂皇的职业等级欺骗视听。在任何学术领域都有一些主题是被承认的而另一些主题却不是,不幸的是被承认的主题与不被承认的主题之间的区别每每在于它在研究论文中的描述听起来多么有难度,而不是取得一个好的结果的意义有多么重要。像文艺做品就是一个明显的例子,研究文艺做品的人不多说对做者哪怕有一丁点用处的话。
虽然在科学里状况要好一些,可是你能够作的事情不少,能够产生好的语言的事情却不多,之间的重叠少得可怜。(Olin Shivers曾经痛批过这一点。)例如,数据类型的研究一直以来都是研究论文的不竭的题材源泉,可是对于静态类型将要排除宏类型(我认为,若是没有宏,将没有哪一种语言值得使用)的事却漠不关心。
看当前语言发展趋势,语言设计正在趋于被看成开源项目来开发而不是看成研究课题来作,并且开发者也正在趋于编写应用程序的程序员而不是编译器的编写者。这是一个好的趋势,我但愿它继续下去。
不像物理学——几乎不可能预测它一百年后的样子,我认为原则上是可能如今设计一种语言符合一百年后的用户须要的。
可能的设计出那种语言的方式是仅凭你的意愿写下程序,不用考虑是否有编译器能够解释它,也不用考虑是否有硬件能运行它。当你写程序的时候你能够假想有无限的资源(我想咱们如今应该能够想象获得一百年后有无限资源的情景)。
人们会凭意愿写出什么样的程序呢?应该是所需的最少的劳动。要完全的最少:在你的思惟尚未被你如今习惯了的编程的语言影响的“前提”下所想到的所需的最少的劳动。不过你已经习惯了的编程语言的影响是如此的广泛深入,以至于你不得不作出巨大的努力来克服它。你能够尽可能想象一下咱们这些懒鬼怎么去用最少的努力表达一个程序。事实上,由于咱们全部的想法是如此受到咱们的思惟所用的语言的限制,因此程序的简洁一点的表达都让咱们很是吃惊。你须要作的是发现和觉醒,不是天然而然地陷入其中。
一个有用的诀窍是用程序的长度来做为你编程劳动的多少的近似值——固然不是按字符来计算的长度,而是按独立语法元素(基本上就是是解析树的大小)来计算的长度。要说最短的程序就标志是你最少编程劳动也不彻底准确,可是它做为一个简洁性的指标是寥胜于无的。那么,语言设计的法则就变成了:看看一个程序并问一问,是否还有别的方法能够把程序写得更短?
实际上,用不可想象的百年语言写程序会根据你接近它的内核的程度不一样其结果也有所不一样。你能够如今就写出排序程序,可是很难预测一百年后将须要什么样的代码库(library),大概会出现许多新领域的代码库。譬如,若是到时候SETI@home(译注:Search for Extra Terrestrial Intelligence at home,是由美国加州大学伯克利分校创建的一项旨在利用连入Internet的成千上万台计算机的闲置能力搜寻地外文明的巨大试验)还有效,咱们就将须要与外星人(alien)通讯的代码库。固然前提是他们也足够发达,也能用XML来沟通。
极端一点,我认为你今天就有可能设计出那种核心语言。有人可能会说,实际上它已经早在1958年(译注:J.McCarthy于1958年提出了Lisp的想法,并于同年跟他的学生们一块儿进行最初的实现工做。)就被设计好了。
假设咱们今天就能够用所谓百年语言,咱们会用它来编程吗?为了回答这个问题,让咱们回顾一下从前,若是咱们当前所用的编程语言在1960年就可用,那时的人们会用它们吗?
从各个方面来说,答案都是否认的。当今的语言是创建在1960年还不存在的一些基础上的。例如,像Python这种语言中每行的缩进是颇有意义的规定,可是这种规定在那时候的打印终端上是行不通的。这样的问题暂且不说,试想一下,那时候的程序都是写在纸上的,1960年代的程序员们会喜欢用咱们今天所用的语言来写程序么?
我认为仍是会的。虽然对于那些已经把史前古老的语言根深蒂固地融入到他们对计算机程序的认识中的缺少想象力的人来讲,确实很难。(天哪!没有指针方法那怎么操纵数据?没有goto怎么实现流程图?)可是我认为最聪明的程序员们将很天然地使用咱们当今所用的大多数语言——若是他们当时有这些语言的话。
若是咱们如今就拥有了百年语言,起码它会产生伟大的伪代码。用它来写程序又会怎么样呢?既然百年语言须要生成快的生成码以适应某些应用,那么大概它应该也能生成足够可接受的高效的生成码来适应咱们如今的硬件。只是咱们或许不得不比一百年后的用户要给出更多的优化建议,但那也是划算的。
咱们如今有了两个观念,若是你把他们结合起来,会产生有趣的可能性:(1)原则上讲,百年语言是能够在今天被设计出来的;(2)这种语言若是今天已经存在,那么用它来编程应当是不错的。当你看到像这样明摆着的两个观念,就不难想到:为何不如今就试着用百年语言来编程呢?
若是你是作语言设计工做的,我认为你最好有这种目标,而且把它牢记于心。当你学驾驶的时候,他们教给你的基本原则之一就是不要只是让你的引擎盖对准道路上的行道线,而是要把目光瞄准远处。哪怕你只是在乎下一个10英尺会发生的事,也应该这样。我想咱们在编程语言方面能够也应该这样作。
Notes
我相信Lisp语言中的Machine Lisp是第一个将“声明(动态变量除外)只是优化建议,而不会改变正确程序的意义”这一原理具体化的语言,而Common Lisp首次将其明确地阐释了出来。
感谢Trevor Blackwell,Robert Morris 和Dan Giffin,他们阅读了本文的初稿,感谢Guido van Rossum,Jeremy Hylton和Python社团的其余全体成员,是他们邀请我在Python大会上讲话。