技术开发 频道

一百年后 人们使用什么语言开发软件?

  【IT168 评论】很难预测一百年后的人类生活,只有少数几件事是可以确定的。那时,汽车将具备低空飞行能力,城市规划的法规将放宽,大楼可以造到几百层,大街上一天到晚看不见太阳,女性个个都学过防身术。本文只想讨论其中的一个细节:一百年后,人们使用什么语言开发软件?

  为什么这个问题值得思考?

  原因不是我们最终会用上这些语言,而是幸运的话,我们从现在开始就能用上这些语言。

  我认为,编程语言就像生物物种一样,存在一个进化的脉络,许许多多分支最终都会成为进化的死胡同。这种现象已经发生了。Cobol语言曾经流行一时,但是现在看来没有任何后续语言继承它的思想。它就像尼安德特人①一样,进化之路已经走到了尽头。(①尼安德特人(Neanderthal),一种生活在欧洲的古人类,三万多年前已经全部灭绝。——译者注)

  我预言Java也会如此。有人写信说:“你怎么能说Java不会成功呢?它已经成功了。”我觉得这要看你的成功标准是什么。如果标准是相关书籍的出版量,或者是相信学会Java就能找到工作的大学生数量,那么Java确实已经成功了。当我说Java不会成功时,我的意思是它和Cobol一样,进化之路已经走到了尽头。

  这只是我的猜测,未必正确。这里的重点不是看衰Java,而是提出编程语言存在一个进化的脉络,从而引导读者思考,在整个进化过程中,某一种语言的位置到底在哪里?之所以要问这个问题,不是为了一百年后让后人感叹我们曾经如此英明,而是为了找到进化的主干。它会启发我们去选择那些靠近主干的语言,这样对当前的编程最有利。

  无论何时,选择进化的主干可能都是最佳方案。要是你不幸选错了,变成了一个尼安德特人,那就太糟了。你的对手克鲁马努人时不时就会来攻打你,把你的食物全部偷走。

  这就是我想找出一百年后的编程语言的原因。我不愿意押错赌注。

  编程语言的进化与生物学进化的区别

  编程语言的进化与生物学进化还是有区别的,因为不同分支的语言会发生聚合。比如,Fortran分支看来正在与Algol的继承者聚合。理论上,不同的生物物种也可能发生聚合,但是可能性很低,所以大概从来没有真正出现过。

  编程语言之所以可能出现聚合,一个原因是它的概率空间②比较小,另一个原因是它的突变不是随机的。语言的设计者们总是有意识地借鉴其他语言的设计思想。(①Algol语言诞生于20世纪50年代,是最早的计算机语言之一,对后来的许多语言产生了极大的影响。——译者注)

  对于语言设计者来说,认清编程语言的进化路径特别有用,因为这样就可以照着样子设计语言了。这时,认清进化的主干就不仅有助于识别现存的优秀语言,还可以把它当作设计语言的指南。

  任何一种编程语言都可以分成两大组成部分:基本运算符的集合(扮演公理的角色)以及除运算符以外的其他部分(原则上,这个部分可以用基本运算符表达出来)。

  我认为,基本运算符是一种语言能否长期存在的最重要因素。其他因素都不是决定性的。这有点像买房子的时候你应该先考虑地理位置。别的地方将来出问题都有办法弥补,但是地理位置是没法变的。

  慎重选择公理还不够,还必须控制它的规模。数学家总是觉得公理越少越好,我觉得他们说到了点子上。你仔细审视一种语言的内核,考虑哪些部分可以被摒弃,这至少也是一种很有用的训练。在长期的职业生涯中,我发现冗余的代码会导致

  更多冗余的代码,不仅软件如此,而且像我这样性格懒散的人,我发现在床底下和房间的角落里这个命题也成立,一件垃圾会产生更多的垃圾。

  我的判断是,那些内核最小、最干净的编程语言才会存在于进化的主干上。一种语言的内核设计得越小、越干净,它的生命力就越顽强。

  ②概率空间是一个数学术语,大致指概率的可能取值范围。这里的意思是,不管编程语言怎么变,它的形式总是很有限的。——译者注

  当然,猜测一百年后人们使用什么编程语言,这本身就是一个很大的假设。也许一百年后人类已经不编程了,或者直接告诉计算机想做什么,计算机就会自动完成。

  编程语言进化缓慢的原因

  不过,到目前为止,计算机智能并没有取得太大进展。我猜测一百年后,人们还是使用与现在差不多的程序指挥计算机。可能有一些我们今天需要编程解决的问题,那时已经不需要编程了,但是我想,那时还会存在大量与今天一样的编程任务。

  你可能认为只有那些自以为是的人才会去预言一百年后的技术。但是,请不要忘记,软件发展的历史已经走过了50年。在这50年中,编程语言的进化其实是非常缓慢的,因此展望一百年后的语言并不是虚无缥缈的想法。

  编程语言进化缓慢的原因在于它们并不是真正的技术。语言只是一种书写法,而程序则是一种严格符合规则的描述,以书面形式记录计算机应该如何解决你的问题。所以,编程语言的进化速度更像数学符号的进化速度,而不像真正的技术(比如交通或通信技术)的进化速度。数学符号的进化是缓慢的渐变式变化,而不是真正技术的那种跳跃式发展。

  无论一百年后的计算机是什么样子,我们基本上可以断定它们的运行速度一定会快得多。如果摩尔定律依然成立,一百年后计算机的运行速度将是现在的74乘以10的18次方倍(准确地说是73786976294838206464倍)。真是让人难以想象。不过实际上更现实的预测并不是速度会提高这么多,而是摩尔定律最终将不成立。不管是什么东西,如果每18个月就增长一倍,那么最后很可能会达到极限。但那时的计算机比现在快得多大概是毫无疑问的。即使最后只是略微快了100万倍,也将实质性地改变编程的基本规则。如果其他条件不变,现在被认为运行速度慢的语言(即运行的效率不高)将来会有更大的发展空间。那时,依然会有对运行速度要求很高的应用程序。我们希望计算机解决的有些问题其实是计算机本身引起的。比如,计算机处理视频的速度取决于生成这些视频的另一台计算机。此外,还有一些问题本身就要求无限快的处理能力,比如图像渲染、加密/解密、模拟运算等。

  既然在现实中一些应用程序本身的效率较低,而另一些应用程序会耗尽硬件提供的所有运算能力,那么有了更快速的计算机就意味着编程语言不得不应付更多的极端情况,涵盖更大范围的效率要求。我们已经看到这种情况发生了。要是以几十年前的标准衡量,有一些使用新语言开发的热门应用程序对硬件资源的浪费非常惊人。

  不仅编程语言有这种现象,这实际上是一种普遍的历史趋势。随着技术的发展,每一代人都在做上一代人觉得很浪费的事情。30年前的人要是看到我们今天如此随意地使用长途电话,一定会感到震惊。100年前的人要是看到一个普通的包裹竟然也能享受一天内从波士顿发件、途经孟菲斯、抵达纽约的待遇,恐怕就要更震惊了。

  我已经预测了,一旦未来硬件的性能大幅提高将会发生什么事。新增加的运算能力都会被糟蹋掉。

  在我学习编程的年代,计算机还是稀罕玩意。我记得当时使用的微机型号是TRS-80,它的内存只有4K,为了把BASIC程序装入内存,我不得不把源码中的空格全部删除。我一想到那些极其低效率的软件,不断重复某些愚蠢的运算,把硬件的计算能力全部占用,就感到无法忍受。但是,我的这种反应是错的,我就像某个出身贫寒的穷孩子,一听到要花钱就舍不得,即使把钱用在重要场合(比如去医院看病)都觉得很难接受。

  某些浪费确实令人厌恶。比如有人就很讨厌SUV(运动型多用途车),即使它采用可再生的清洁能源也改变不了看法,因为SUV来自一个令人厌恶的想法(如何使得小货车看上去更有男子汉气概)。但是,并非所有的浪费都是坏的。既然如今的电信基础设施已经如此发达,再掐着时间打长途电话就有点锱铢必较了。如果有足够的资源,你可以将长途电话和本地电话视为同一件事,一切会变得更轻松。

  浪费可以分成好的浪费和坏的浪费。我感兴趣的是好的浪费,即用更多的钱得到更简单的设计。所以,问题就变成了如何才能充分利用新硬件更强大的性能最有利地“浪费”它们?

  对速度的追求是人类内心深处根深蒂固的欲望。当你看着计算机这个小玩意,就会不由自主地希望程序运行得越快越好,真的要下一番功夫才能把这种欲望克制住。设计编程语言的时候,我们应该有意识地问自己,什么时候可以放弃一些性能,换来一点点便利性的提高。

  很多数据结构存在的原因都与计算机的速度有关。比如,今天的许多语言都同时有字符串和列表。从语义上看,字符串或多或少可以理解成列表的一个子集,其中的每一个元素都是字符。那么,为什么还需要把字符串单列为一种数据类型呢?完全可以不这么做。只是为了提高效率,所以字符串才会存在。但是,这种以加快运行速度为目的、却使得编程语言的语义大大复杂的行为,很不可取。编程语言设置字符串似乎就是一个过早优化的例子。

  如果我们把一种语言的内核设想为一些基本公理的集合,那么仅仅为了提高效率就往内核添加多余的公理,却没有带来表达能力的提升,这肯定是一件很糟的事。没错,效率是很重要,但是我认为修改语言设计并不是提高效率的正确方法。

  正确做法应该是将语言的语义与语言的实现予以分离。在语义上不需要同时存在列表和字符串,单单列表就够了。而在实现上做好编译器优化,使它在必要时把字符串作为连续字节的形式处理。① (①我相信,LispMachineLisp(Lisp语言的一种方言)是第一个具体表达这样一种观点的语言:变量的声明(除了动态类型变量之外)只是优化的建议,对一个正确程序本身的含义不构成影响。CommonLisp(Lisp语言的另一种方言)则好像第一个明确提出了这一点。)

  对于大多数程序,速度不是最关键的因素,所以你通常不需要费心考虑这种硬件层面上的微观管理。随着计算机速度越来越快,这一点已经越发明显了。

  语言设计时,对实现方式少作限制还会使得程序具备更大的灵活性。

  语言的规格发生变化不仅是无法避免的,也是合理的。通过编译器的处理,按照以前规格开发的软件就会照常运行,这就提供了灵活性。

  essay(论文)这个词来自法语的动词essayer,意思是“试试看”。从这个原始意义来说,论文就是你写一篇文章,试着搞清楚某件事。软件也是如此。我觉得一些最好的软件就像论文一样,也就是说,当作者真正开始动手写这些软件的时候,他们其实不知道最后会写出什么结果。

  Lisp语言的黑客早就明白数据结构灵活性的价值。我们写程序的第一版时,往往会把所有事情都用列表的形式处理。所以,这些最初版本可能效率低下得惊人,你必须努力克制自己才能忍住不动手优化它们,这就好像吃牛排的时候,必须努力克制自己才能不去想牛排是从哪里来的一样,至少对我来说是这样的。

  一百年后的程序员最需要的编程语言就是可以让你毫不费力地写出程序第一版的编程语言,哪怕它的效率低下得惊人(至少按我们今天的眼光来看是如此)。他们会说,他们想要的就是很容易上手的编程语言。

  效率低下的软件并不等于很烂的软件。一种让程序员做无用功的语言才真正称得上很烂。浪费程序员的时间而不是浪费机器的时间才是真正的无效率。随着计算机速度越来越快,这会变得越来越明显。

  我觉得,放弃字符串类型已经是大家可以接受的想法了。Arc语言已经这样做了,看上去效果不错。以前用正则表达式很难描述的一些操作,现在用回归函数可以表达得很简单。

  这种数据结构的扁平化趋势会怎么发展?我极其努力地设想各种可能,得到的结果甚至令我自己都吓了一跳。比如,数组会不会消失?毕竟数组只是散列表的一个子集,其特点就是数组的键全部都是整数向量。进一步说,散列表本身会不会被列表取代呢?

  还有比这更惊人的预言。在逻辑上其实不需要对整数设置单独的表示法,因为可以把它们也看作列表,整数n可以用一个n元素的列表表示。这一样能完成数学运算,只是效率低得让人无法忍受。编程语言会发展到放弃基本数据类型之一的整数这一步吗?我这样

  问并不是真的要你严肃思考这个问题,更多的是希望打开你对未来的思路。我只是提出一种假想的情况:如果一股不可抗拒的力量遇到了一个不可移动的物体,会发生什么事。具体就本文而言:一种效率低得不可想象的语言遇到了性能强大得不可想象的硬件,会发生什么事。我看不出放弃整数类型有什么不妥。未来相当漫长。如果我们想要减少语言内核中基本公理的数目,不妨把眼光放得远一点,想一想如果时间变量t趋向无限会怎么样。一百年是一个很好的参考指标,如果你觉得某个想法在一百年后仍然可能是难以令人接受,那么也许一千年后它也依然难以令人接受。

  让我说清楚,我的意思不是说所有的整数运算都用列表来实现,而是说语言的内核(不涉及任何编译器的实现)可以这样定义。在现实中,任何进行数学运算的程序可能都是以二进制形式表示数字,但是这属于编译器的优化,而不属于语言内核语义的一部分。

  另一种消耗硬件性能的方法就是,在应用软件与硬件之间设置很多的软件层。这也是我们已经看到的一种趋势,许多新兴的语言就被编译成字节码①。比尔·伍兹曾经对我说,根据经验判断,每增加一个解释层,软件的运行速度就会慢一个数量级。但是,多余的软件层可以让编程灵活起来。(①字节码(bytecode)是已经经过编译但是需要进一步处理才能变成机器码的中间代码。它的好处是与硬件和软件环境无关,在编译器的配合下,可以在不同的操作系统上运行。字节码的典型运用就是Java语言。——译者注)

  Arc语言

  (②Arc是Lisp的一种方言,由本书作者提出,目前由他本人和罗伯特·莫里斯负责开发。——译者注)

  最初的版本就是一个极端的例子,它的层很多,运行速度非常慢,但是确实带来了相应的好处。Arc是一个典型的“元循环”(metacircular)解释器,在CommonLisp的基础上开发,很像约翰·麦卡锡在他经典的Lisp论文中定义的eval函数。Arc解释器一共只有几百行代码,所以很便于理解和修改。我们采用的CommonLisp版本是CLisp,它本身是在另一个字节码解释器的基础上开发的。所以,我们一共有两层解释器,最上面那层效率低下得惊人,但是语言本身是能用的。我承认只是勉强可用,但是确实能用。

  即使是应用程序,使用多层形式开发也是一种很强大的技巧。自下而上的编程方法意味着要把软件分成好几层,每一层都可以充当它上面那一层的开发语言。这种方法往往会产生更小、更灵活的程序。它也是通往软件圣杯——可重用性(reusability)——的最佳路线。从定义上看,语言就是可以重用的。在编程语言的帮助下,你的应用程序越是采用这种多层形式开发,它的可重用性就越好。

  可重用性这个概念多多少少与20世纪80年代兴起的面向对象编程有些关联。不管怎样寻找证据,也不可能把这两件事完全分开。某些使用面向对象编程开发出来的软件确实具有可重用性,但是这不是因为它使用了面向对象编程,而是因为它的开发方法是自下而上的。以函数库为例,它们具有可重用性,是因为它们属于语言的一部分,而不是因为它们采用面向对象或者其他编程方法。

  顺便说一句,我不认为面向对象编程将来会消亡。我觉得,除了某些特定的领域,这种编程方法其实没有为优秀程序员带来很多好处,但是它对大公司有不可抗拒的吸引力。面向对象编程使得你有办法对一团乱码似的代码进行可持续性开发。通过不断地打补丁,它让你将软件一步步做大。大公司总是倾向于采用这样的方式开发软件。我预计一百年后也是如此。

0
相关文章