画家与黑客
№.19 编程语言解析——编程语言怎么就火了?
所有的机器都有一张操作命令清单,让我们控制它们。例如mp3上的打开、关闭、调节音量、播放、暂停等。计算机也是一种机器,它的指令清单就是机器语言(machine language)。
计算机诞生之初,所有程序就是一条条机器语言的命令,后来被改成了更容易理解的汇编语言。比如,计算机内部的加法表达方式是11001101,而在汇编语言中则为add。
机器语言和汇编语言的共同问题是,只能执行一些简单的操作,代码还比较复杂。代码越多,出现bug的可能性就越大。例如,我们想让蜂鸣器响10次,用机器语言写的程序是这样的:
a 将数字10存入地址0(注:正确的写法应该是9,编程语言中一般是从0开始计数,这样写为了突出代码易产生bug的特性)
如果内存地址0的值为负数,跳到b行
蜂鸣器发出声音
将内存地址0的值减1
跳到a行
b ……程序的其他部分
后来,程序员找到了一个“助手”——编译器。编译器本身就是一个程序,作用是将简单的易书写的程序转换为硬件可以理解的语言(还有一个“助手”叫解释器,不同于编辑器全部翻译再运行,而是实时翻译,一行一行运行)。这种方便书写的语言就是高级语言。比如,还是让蜂鸣器响10次,只需要写:
dotimes 10 蜂鸣器响(dotimes 是Lisp语言中表示循环处理的命令。)
程序变得更简短了,如果出现了错误,也更容易发现。另外,高级语言也使程序有了可移植性。不同型号计算机的机器语言是不完全相同的,如果采用高级语言,你就不用针对每种机型写代码,重写编译器就成。
编译器处理的高级语言代码叫做源码,处理之后的机器码叫做目标码(可见于在市场上售卖的大部分商业软件)。目标码可读性很差,基本上相当于加密。后来出现了开放源代码的潮流:公开可以随意修改的源码。开源让我们可以修改软件,同时也能自己动手修正bug。
绝大多数程序员在绝大多数时候,都会选择高级语言编程。现在的高级语言大概有几百种,比较出名有:Fortran、Lisp、Cobol、Basic、C、Pascal、Smalltalk、C++、Java、Perl和Python(注:这是2004年的情况了)。不同的机器语言的指令集基本相同,但不同的高级语言开发程序的模式差别却相当大。
究竟该用哪种语言,一直以来,都很有争议。一些黑客只喜欢自己用的语言,反感其他所有语言;另一些黑客则表示所有的语言都一样。语言之间确实差别很大,但很难确定地说哪种语言是最好的。(编者注:后文关于编程语言的评论,均为原书作者观点,大家可以听听他的看法,至于信或不信请自行决定。)
高级语言也有层次。例如,C语言是一种低层次语言,接近硬件,堪称可移植的汇编语言,而Lisp的层次则相当高。一般情况,层次越高,越有利于编程,但也并非绝对。接近机器语言的低层次语言运行速度更快,大多数操作系统都是用C语言编写的,因为大家都觉得操作系统越快越好。
编程语言之间还有一个议题是静态类型语言与动态类型语言之争。用静态语言写代码,必须清晰地定义每个变量的类型,而在动态语言中,你可以随时更改变量的类型。前者的拥护者认为这样可以防止bug,且能帮助编译器生成更快的代码;后者的粉丝则认为静态类型对程序构成了限制。这个争论一直没有停息过,不过也要看需求。有人希望编程语言可以防止程序员干傻事,有人则认为编程语言应该让程序员能够心想事成。所以,美国国防部很看中Java。
另一个争论的热点则是面向对象编程(一种计算机编程架构)。打个比方,你需要写一个程序计算二维图形的面积。有两种实现方式:方法一,用一整块代码判断遇到的是什么图形,然后再用相应的公式来计算面积;方法二,写两段代码,一段是解决圆形类的,一段是解决正方形类的,每个类里面用一小块代码计算该类图形的面积。方法二即是面向对象的编程方式(简单可理解为通用型和专用型)。面向对象编程的优点在于,如果你需要修改程序,比如计算三角形的面积,你只需要再另外加一块相应的代码就可以了,甚至都不需要修改另外的两部分。当然,这也是有缺点的,增加的代码不用考虑其他部分,结果往往导致写出性能不佳甚至有副作用的代码。
关于面向对象编程的争论并没有静态、动态类型之争那样泾渭分明。因为编程时,静态类型和动态类型是必须二选一的,但面向对象编程则只是程度不同的问题。我认为,在编程时,应该选择“允许”(而非“强迫”)使用面向对象编程的语言,因为用不用是你的自由。
拉里·瓦尔为了使管理机房的工作变得更方便,用业余时间创造了Perl语言。他给了很多黑客启发:为什么不自己动手设计一款语言呢?结果有了一些“头重脚轻”的语言:它们的内核设计一般,但是却有着强大且好用的函数库,可以很方便地解决一些特定问题。
很快,语言变得多样化了。编程语言的文艺复兴时代到来,“战争”也随之发生。
真是百花齐放、百家争鸣,我觉得这是程序员的黄金时代。
№.20 一百年后的编程语言——为什么不从现在就开始这样做呢?
本节我想探讨的问题是:一百年后,人们会使用什么语言开发软件?
如果幸运地找到答案,我们从现在就可以开始用上这些语言。
我认为,编程语言和自然生物一样,存在着进化脉络。在我看来,Java的进化之路已到尽头。回到开头的问题,其提出是为了找到编程语言的进化脉络,启发我们选择那些靠近主干的语言,这对当前的编程是有利的。
编程语言的进化同生物的进化还是有区别的,因为不同分支的语言会发生聚合。比如,Fortran分支看来正在与Algol(最早的计算机语言之一,对后来的许多语言产生了极大的影响)的继承者聚合。编程语言发生聚合,是因为编程语言的形式有限,怎么变都差不多是那几种。另外,编程语言的突变也不是随机的,一般是借鉴其他语言的设计思想。
对于设计者来说,认清进化的主干有助于识别现存的优秀语言,还可以把它当作设计语言的指南。
编程语言由两大部分组成:基本运算符的集合(扮演公理的角色)以及除运算符以外的其他部分(原则上,这部分可以用基本运算符表达出来)。基本运算符是一种语言能否长期存在的最重要因素,就像数学家认为公理越少越好一样,基本运算符也是如此。
所以,我的判断是——那些内核最小、最干净的编程语言才会存在于进化的主干上。内核设计得越小、越干净,这种语言的生命力就越顽强。
在软件走过的50多年里,编程语言的进化其实是非常缓慢的,因此展望一百年后的语言是可行的。编程语言进化如此缓慢是因为它们并不是真正的技术。语言是一种书写方法,它只能像数学符号那样渐变式变化。
下面,我们探讨一些细节:
首先,可以预料的是,一百年后计算机的运行速度将会快很多。如果其他条件不变,现在被认为运行速度慢的语言(即运行效率不高)将会有更大的发展空间。对实现方式少作限制的语言,在编程时会具备更大的灵活性。另外,硬件性能的大幅提高可以让我们在性能上做一些妥协,换来便利性的提高,所以一些由于效率低下但编写方便的语言会被重新考虑(要知道,程序员的时间要比机器的时间更有价值)。
放弃一些数据类型,在一百年后应该也是可以实现的。Arc语言已经放弃字符串类型了,看上去效果还不错。一百年后,性能分析器将变得越来越重要,它能指导提升应用软件运行速度。
一百年后的编程语言,在理论上,今天就可以设计出来。如果现在用这种语言编程,纵然我们不能直接用它开发软件(硬件跟不上),但用它为一些应用程序生成快速代码还是能用得到的。所以,为什么不现在就动手尝试写出一百年后的编程语言呢?
在设计编程语言时,我们可以牢记这个目标。好比要把车开直,你看的是远处的点。
№.21 拒绝平庸——你的对手还很强大
简单说一下,Viaweb网站主要有两个部分——编辑器和订单处理系统。编辑器主要供用户搭建自己的网站,这个部分是用Lisp语言开发的。这是第一个用Lisp语言开发的大型应用程序。
埃里克·雷蒙德的《如何成为一名黑客》(How to Become a Hacker)中谈到,如果你想当黑客,可以从Python和java入手,因为比较容易掌握。然后开始学C和Perl,C可以用来对付Unix系统,Perl则可以用来管理系统和开发CGI脚本。最后,他建议,把黑客作为人生目标的人,应该学习Lisp:
Lisp很值得学习。你掌握它以后,会感到它给你带来的极大启发。这会大大提高你的编程水平,使你成为一个更好的程序员。尽管在实际工作中极少会用到Lisp。
埃里克·雷蒙德的观点也代表了大多数人对Lisp的看法。但这里面有一个矛盾:Lisp语言能让你成为更好程序员,但你却不用它,这难道不奇怪吗?
对于技术的选择,我们应该考虑怎样的技术能最好地完成工作。我和莫里斯都很了解Lisp语言,虽然其他人都是用C++和Perl做开发。但我们相信自己的直觉,坚持用Lisp开发Viaweb。
互相模仿对于大公司而言,是可行的。但对于创业公司而言,却意味着关门倒闭。因为大公司只要每件事做到大公司的平均水平,就能得到大公司的平均成长结果(大约10%)。但创业公司的生存率远低于50%,所以,创业公司最好做一些独特的事情,这可能会提高你的生还概率。
我们当时选择Lisp,主要基于以下考虑:首先,公司需要快速开发出新产品,而Lisp语言非常适合快速开发。其次,一般公司都不会使用这种语言,如果Viaweb用了,这可能会发展成为产品优势。最后,Lisp是一种抽象层次非常高的语言,效率很高,不需要庞大的开发团队,这会降低成本。
Viaweb前前后后遇到二三十个竞争对手,获得胜利的总是Viaweb。互联网软件的本质打败了一批桌面软件的竞争对手;在功能上总是优于使用CGI脚本的竞争对手;至于在引入新功能方面,效果更佳,通常竞争对手才发布新闻稿,在一两天内,Viaweb就可以发布自己的新版本。商场如战场,在竞争中,你的对手无法理解你的技术优势,摸不透你,你的胜算就增加了。
Lisp最核心的优势在哪里?一句话:Lisp是目前最强大的编程语言。
它没有得到广泛使用,是因为编程语言会让使用者形成难于改变的习惯性思维。编程语言的编程能力是有差异的,最直观的是高级语言比机器语言更强大好用。今天的大多数程序员都是使用某种高级语言编程,然后让编译器把它翻译成机器语言。这种流程进而影响了硬件,硬件指令集都是针对编译器而不是程序员设计的。
最终选择使用哪种语言编程,有很多情况需要考虑。例如,如果你在开发的软件需要与另一个程序紧密配合,那么选择与后者保持一致的语言来编程是比较方便的。如果你的程序只需要做一些简单的运算,那么选择接近机器语言的低层次语言是比较方便的。如果你的程序很短,且只需要用于一次性的特定场合,那么根据你的需要选择具有强大函数库的语言会比较好。总体而言,选择强大的、效率在可接受范围内的编程语言都是正确的。
高级语言与低级语言相比,其优势在于功能更强大,如果一开始接触的语言不能满足你的编程需要,你可能会寻找并学习一门更高级的语言。一旦找到包含所有你需要功能的语言,你就会觉得这门语言就够好、够用了。到了一定年龄后,程序员更是极少更换自己的编程语言。此时就算看到了更高级的语言,你也可能仅仅觉得这些语言很奇怪,而不会去深入了解。只有懂得最强大的那种语言的人,才能洞悉所有语言的优劣,所谓一览众山小。
我最开始是用Basic语言编程的,很弱,甚至不支持递归(笔者注:在运行的过程中调用自己)。但当时觉得没有递归就没有吧,对使用并没有影响。
后来,接触了Lisp,才发现Lisp的宏是独一无二的,很多语言甚至都没有。Lisp有一种很奇特的语法,或者说它根本没有语法。一般的源代码程序经过编译器解析会生成解析树,Lisp的奇特之处在于,你可以写程序控制解析树,进行任意的存取操作。这种程序就叫宏,它们可以用来生成其他程序。
Viaweb编译器的源码大约有20%-25%是宏。它们比普通的Lisp函数难写。这就意味着这个程序至少有20%-25%代码的功能无法轻易用其他语言实现。这就是我们给竞争对手设下的障碍,至少不能轻易追赶上。
我写此文的目的不是想改变任何人的观点,而是想让那些有兴趣学习或正在学习Lisp语言的人放心。即使使用者不多,可能学会了也没什么用,但Lisp语言是强大的。在商业竞争中使用,其优势就会显现出来。
普通的编程语言正在主导一切,我不建议大家挑战这种习惯势力,相反,我们应该向日本合气道(一种日本武术,主要特点是“以柔克刚”“不主动攻击”等)选手学习。
如果你为创业公司工作,那么这里有一个评估竞争对手的妙招——关注他们的招聘职位。我读过大量竞争对手的招聘职位。基本上,这样就可以大致了解,哪些公司是值得关注的,哪些是不用在意的。总结:职位描述里使用大量IT词汇的,内容越多,这家公司越构不成威胁;要求应聘者有Oracle数据库经验的公司,你可以放弃关注了;招聘C++或Java程序员的公司,对你也不会构成威胁;如果招聘Perl或Python程序员,那就需要稍微注意了,他们会存在威胁,至少听起来,这是一家由黑客控制的技术公司;如果见到一家招聘Lisp黑客的公司,那么,对手来了。