№.19 編程語言解析——編程語言怎么就火了?
所有的機(jī)器都有一張操作命令清單,讓我們控制它們。例如mp3上的打開、關(guān)閉、調(diào)節(jié)音量、播放、暫停等。計算機(jī)也是一種機(jī)器,它的指令清單就是機(jī)器語言(machine language)。
計算機(jī)誕生之初,所有程序就是一條條機(jī)器語言的命令,后來被改成了更容易理解的匯編語言。比如,計算機(jī)內(nèi)部的加法表達(dá)方式是11001101,而在匯編語言中則為add。
機(jī)器語言和匯編語言的共同問題是,只能執(zhí)行一些簡單的操作,代碼還比較復(fù)雜。代碼越多,出現(xiàn)bug的可能性就越大。例如,我們想讓蜂鳴器響10次,用機(jī)器語言寫的程序是這樣的:
a? ? 將數(shù)字10存入地址0(注:正確的寫法應(yīng)該是9,編程語言中一般是從0開始計數(shù),這樣寫為了突出代碼易產(chǎn)生bug的特性)
如果內(nèi)存地址0的值為負(fù)數(shù),跳到b行
蜂鳴器發(fā)出聲音
將內(nèi)存地址0的值減1
跳到a行
b? ? ? ……程序的其他部分
后來,程序員找到了一個“助手”——編譯器。編譯器本身就是一個程序,作用是將簡單的易書寫的程序轉(zhuǎn)換為硬件可以理解的語言(還有一個“助手”叫解釋器,不同于編輯器全部翻譯再運行,而是實時翻譯,一行一行運行)。這種方便書寫的語言就是高級語言。比如,還是讓蜂鳴器響10次,只需要寫:
dotimes 10 蜂鳴器響(dotimes 是Lisp語言中表示循環(huán)處理的命令。)
程序變得更簡短了,如果出現(xiàn)了錯誤,也更容易發(fā)現(xiàn)。另外,高級語言也使程序有了可移植性。不同型號計算機(jī)的機(jī)器語言是不完全相同的,如果采用高級語言,你就不用針對每種機(jī)型寫代碼,重寫編譯器就成。
編譯器處理的高級語言代碼叫做源碼,處理之后的機(jī)器碼叫做目標(biāo)碼(可見于在市場上售賣的大部分商業(yè)軟件)。目標(biāo)碼可讀性很差,基本上相當(dāng)于加密。后來出現(xiàn)了開放源代碼的潮流:公開可以隨意修改的源碼。開源讓我們可以修改軟件,同時也能自己動手修正bug。
絕大多數(shù)程序員在絕大多數(shù)時候,都會選擇高級語言編程?,F(xiàn)在的高級語言大概有幾百種,比較出名有:Fortran、Lisp、Cobol、Basic、C、Pascal、Smalltalk、C++、Java、Perl和Python(注:這是2004年的情況了)。不同的機(jī)器語言的指令集基本相同,但不同的高級語言開發(fā)程序的模式差別卻相當(dāng)大。
究竟該用哪種語言,一直以來,都很有爭議。一些黑客只喜歡自己用的語言,反感其他所有語言;另一些黑客則表示所有的語言都一樣。語言之間確實差別很大,但很難確定地說哪種語言是最好的。(編者注:后文關(guān)于編程語言的評論,均為原書作者觀點,大家可以聽聽他的看法,至于信或不信請自行決定。)
高級語言也有層次。例如,C語言是一種低層次語言,接近硬件,堪稱可移植的匯編語言,而Lisp的層次則相當(dāng)高。一般情況,層次越高,越有利于編程,但也并非絕對。接近機(jī)器語言的低層次語言運行速度更快,大多數(shù)操作系統(tǒng)都是用C語言編寫的,因為大家都覺得操作系統(tǒng)越快越好。
編程語言之間還有一個議題是靜態(tài)類型語言與動態(tài)類型語言之爭。用靜態(tài)語言寫代碼,必須清晰地定義每個變量的類型,而在動態(tài)語言中,你可以隨時更改變量的類型。前者的擁護(hù)者認(rèn)為這樣可以防止bug,且能幫助編譯器生成更快的代碼;后者的粉絲則認(rèn)為靜態(tài)類型對程序構(gòu)成了限制。這個爭論一直沒有停息過,不過也要看需求。有人希望編程語言可以防止程序員干傻事,有人則認(rèn)為編程語言應(yīng)該讓程序員能夠心想事成。所以,美國國防部很看中Java。
另一個爭論的熱點則是面向?qū)ο缶幊蹋ㄒ环N計算機(jī)編程架構(gòu))。打個比方,你需要寫一個程序計算二維圖形的面積。有兩種實現(xiàn)方式:方法一,用一整塊代碼判斷遇到的是什么圖形,然后再用相應(yīng)的公式來計算面積;方法二,寫兩段代碼,一段是解決圓形類的,一段是解決正方形類的,每個類里面用一小塊代碼計算該類圖形的面積。方法二即是面向?qū)ο蟮木幊谭绞剑ê唵慰衫斫鉃橥ㄓ眯秃蛯S眯停?。面向?qū)ο缶幊痰膬?yōu)點在于,如果你需要修改程序,比如計算三角形的面積,你只需要再另外加一塊相應(yīng)的代碼就可以了,甚至都不需要修改另外的兩部分。當(dāng)然,這也是有缺點的,增加的代碼不用考慮其他部分,結(jié)果往往導(dǎo)致寫出性能不佳甚至有副作用的代碼。
關(guān)于面向?qū)ο缶幊痰臓幷摬]有靜態(tài)、動態(tài)類型之爭那樣涇渭分明。因為編程時,靜態(tài)類型和動態(tài)類型是必須二選一的,但面向?qū)ο缶幊虅t只是程度不同的問題。我認(rèn)為,在編程時,應(yīng)該選擇“允許”(而非“強迫”)使用面向?qū)ο缶幊痰恼Z言,因為用不用是你的自由。
拉里·瓦爾為了使管理機(jī)房的工作變得更方便,用業(yè)余時間創(chuàng)造了Perl語言。他給了很多黑客啟發(fā):為什么不自己動手設(shè)計一款語言呢?結(jié)果有了一些“頭重腳輕”的語言:它們的內(nèi)核設(shè)計一般,但是卻有著強大且好用的函數(shù)庫,可以很方便地解決一些特定問題。
很快,語言變得多樣化了。編程語言的文藝復(fù)興時代到來,“戰(zhàn)爭”也隨之發(fā)生。
真是百花齊放、百家爭鳴,我覺得這是程序員的黃金時代。
№.20 一百年后的編程語言——為什么不從現(xiàn)在就開始這樣做呢?
本節(jié)我想探討的問題是:一百年后,人們會使用什么語言開發(fā)軟件?
如果幸運地找到答案,我們從現(xiàn)在就可以開始用上這些語言。
我認(rèn)為,編程語言和自然生物一樣,存在著進(jìn)化脈絡(luò)。在我看來,Java的進(jìn)化之路已到盡頭。回到開頭的問題,其提出是為了找到編程語言的進(jìn)化脈絡(luò),啟發(fā)我們選擇那些靠近主干的語言,這對當(dāng)前的編程是有利的。
編程語言的進(jìn)化同生物的進(jìn)化還是有區(qū)別的,因為不同分支的語言會發(fā)生聚合。比如,F(xiàn)ortran分支看來正在與Algol(最早的計算機(jī)語言之一,對后來的許多語言產(chǎn)生了極大的影響)的繼承者聚合。編程語言發(fā)生聚合,是因為編程語言的形式有限,怎么變都差不多是那幾種。另外,編程語言的突變也不是隨機(jī)的,一般是借鑒其他語言的設(shè)計思想。
對于設(shè)計者來說,認(rèn)清進(jìn)化的主干有助于識別現(xiàn)存的優(yōu)秀語言,還可以把它當(dāng)作設(shè)計語言的指南。
編程語言由兩大部分組成:基本運算符的集合(扮演公理的角色)以及除運算符以外的其他部分(原則上,這部分可以用基本運算符表達(dá)出來)。基本運算符是一種語言能否長期存在的最重要因素,就像數(shù)學(xué)家認(rèn)為公理越少越好一樣,基本運算符也是如此。
所以,我的判斷是——那些內(nèi)核最小、最干凈的編程語言才會存在于進(jìn)化的主干上。內(nèi)核設(shè)計得越小、越干凈,這種語言的生命力就越頑強。
在軟件走過的50多年里,編程語言的進(jìn)化其實是非常緩慢的,因此展望一百年后的語言是可行的。編程語言進(jìn)化如此緩慢是因為它們并不是真正的技術(shù)。語言是一種書寫方法,它只能像數(shù)學(xué)符號那樣漸變式變化。
下面,我們探討一些細(xì)節(jié):
首先,可以預(yù)料的是,一百年后計算機(jī)的運行速度將會快很多。如果其他條件不變,現(xiàn)在被認(rèn)為運行速度慢的語言(即運行效率不高)將會有更大的發(fā)展空間。對實現(xiàn)方式少作限制的語言,在編程時會具備更大的靈活性。另外,硬件性能的大幅提高可以讓我們在性能上做一些妥協(xié),換來便利性的提高,所以一些由于效率低下但編寫方便的語言會被重新考慮(要知道,程序員的時間要比機(jī)器的時間更有價值)。
放棄一些數(shù)據(jù)類型,在一百年后應(yīng)該也是可以實現(xiàn)的。Arc語言已經(jīng)放棄字符串類型了,看上去效果還不錯。一百年后,性能分析器將變得越來越重要,它能指導(dǎo)提升應(yīng)用軟件運行速度。
一百年后的編程語言,在理論上,今天就可以設(shè)計出來。如果現(xiàn)在用這種語言編程,縱然我們不能直接用它開發(fā)軟件(硬件跟不上),但用它為一些應(yīng)用程序生成快速代碼還是能用得到的。所以,為什么不現(xiàn)在就動手嘗試寫出一百年后的編程語言呢?
在設(shè)計編程語言時,我們可以牢記這個目標(biāo)。好比要把車開直,你看的是遠(yuǎn)處的點。
№.21 拒絕平庸——你的對手還很強大
簡單說一下,Viaweb網(wǎng)站主要有兩個部分——編輯器和訂單處理系統(tǒng)。編輯器主要供用戶搭建自己的網(wǎng)站,這個部分是用Lisp語言開發(fā)的。這是第一個用Lisp語言開發(fā)的大型應(yīng)用程序。
埃里克·雷蒙德的《如何成為一名黑客》(How to Become a Hacker)中談到,如果你想當(dāng)黑客,可以從Python和java入手,因為比較容易掌握。然后開始學(xué)C和Perl,C可以用來對付Unix系統(tǒng),Perl則可以用來管理系統(tǒng)和開發(fā)CGI腳本。最后,他建議,把黑客作為人生目標(biāo)的人,應(yīng)該學(xué)習(xí)Lisp:
Lisp很值得學(xué)習(xí)。你掌握它以后,會感到它給你帶來的極大啟發(fā)。這會大大提高你的編程水平,使你成為一個更好的程序員。盡管在實際工作中極少會用到Lisp。
埃里克·雷蒙德的觀點也代表了大多數(shù)人對Lisp的看法。但這里面有一個矛盾:Lisp語言能讓你成為更好程序員,但你卻不用它,這難道不奇怪嗎?
對于技術(shù)的選擇,我們應(yīng)該考慮怎樣的技術(shù)能最好地完成工作。我和莫里斯都很了解Lisp語言,雖然其他人都是用C++和Perl做開發(fā)。但我們相信自己的直覺,堅持用Lisp開發(fā)Viaweb。
互相模仿對于大公司而言,是可行的。但對于創(chuàng)業(yè)公司而言,卻意味著關(guān)門倒閉。因為大公司只要每件事做到大公司的平均水平,就能得到大公司的平均成長結(jié)果(大約10%)。但創(chuàng)業(yè)公司的生存率遠(yuǎn)低于50%,所以,創(chuàng)業(yè)公司最好做一些獨特的事情,這可能會提高你的生還概率。
我們當(dāng)時選擇Lisp,主要基于以下考慮:首先,公司需要快速開發(fā)出新產(chǎn)品,而Lisp語言非常適合快速開發(fā)。其次,一般公司都不會使用這種語言,如果Viaweb用了,這可能會發(fā)展成為產(chǎn)品優(yōu)勢。最后,Lisp是一種抽象層次非常高的語言,效率很高,不需要龐大的開發(fā)團(tuán)隊,這會降低成本。
Viaweb前前后后遇到二三十個競爭對手,獲得勝利的總是Viaweb?;ヂ?lián)網(wǎng)軟件的本質(zhì)打敗了一批桌面軟件的競爭對手;在功能上總是優(yōu)于使用CGI腳本的競爭對手;至于在引入新功能方面,效果更佳,通常競爭對手才發(fā)布新聞稿,在一兩天內(nèi),Viaweb就可以發(fā)布自己的新版本。商場如戰(zhàn)場,在競爭中,你的對手無法理解你的技術(shù)優(yōu)勢,摸不透你,你的勝算就增加了。
Lisp最核心的優(yōu)勢在哪里?一句話:Lisp是目前最強大的編程語言。
它沒有得到廣泛使用,是因為編程語言會讓使用者形成難于改變的習(xí)慣性思維。編程語言的編程能力是有差異的,最直觀的是高級語言比機(jī)器語言更強大好用。今天的大多數(shù)程序員都是使用某種高級語言編程,然后讓編譯器把它翻譯成機(jī)器語言。這種流程進(jìn)而影響了硬件,硬件指令集都是針對編譯器而不是程序員設(shè)計的。
最終選擇使用哪種語言編程,有很多情況需要考慮。例如,如果你在開發(fā)的軟件需要與另一個程序緊密配合,那么選擇與后者保持一致的語言來編程是比較方便的。如果你的程序只需要做一些簡單的運算,那么選擇接近機(jī)器語言的低層次語言是比較方便的。如果你的程序很短,且只需要用于一次性的特定場合,那么根據(jù)你的需要選擇具有強大函數(shù)庫的語言會比較好??傮w而言,選擇強大的、效率在可接受范圍內(nèi)的編程語言都是正確的。
高級語言與低級語言相比,其優(yōu)勢在于功能更強大,如果一開始接觸的語言不能滿足你的編程需要,你可能會尋找并學(xué)習(xí)一門更高級的語言。一旦找到包含所有你需要功能的語言,你就會覺得這門語言就夠好、夠用了。到了一定年齡后,程序員更是極少更換自己的編程語言。此時就算看到了更高級的語言,你也可能僅僅覺得這些語言很奇怪,而不會去深入了解。只有懂得最強大的那種語言的人,才能洞悉所有語言的優(yōu)劣,所謂一覽眾山小。
我最開始是用Basic語言編程的,很弱,甚至不支持遞歸(筆者注:在運行的過程中調(diào)用自己)。但當(dāng)時覺得沒有遞歸就沒有吧,對使用并沒有影響。
后來,接觸了Lisp,才發(fā)現(xiàn)Lisp的宏是獨一無二的,很多語言甚至都沒有。Lisp有一種很奇特的語法,或者說它根本沒有語法。一般的源代碼程序經(jīng)過編譯器解析會生成解析樹,Lisp的奇特之處在于,你可以寫程序控制解析樹,進(jìn)行任意的存取操作。這種程序就叫宏,它們可以用來生成其他程序。
Viaweb編譯器的源碼大約有20%-25%是宏。它們比普通的Lisp函數(shù)難寫。這就意味著這個程序至少有20%-25%代碼的功能無法輕易用其他語言實現(xiàn)。這就是我們給競爭對手設(shè)下的障礙,至少不能輕易追趕上。
我寫此文的目的不是想改變?nèi)魏稳说挠^點,而是想讓那些有興趣學(xué)習(xí)或正在學(xué)習(xí)Lisp語言的人放心。即使使用者不多,可能學(xué)會了也沒什么用,但Lisp語言是強大的。在商業(yè)競爭中使用,其優(yōu)勢就會顯現(xiàn)出來。
普通的編程語言正在主導(dǎo)一切,我不建議大家挑戰(zhàn)這種習(xí)慣勢力,相反,我們應(yīng)該向日本合氣道(一種日本武術(shù),主要特點是“以柔克剛”“不主動攻擊”等)選手學(xué)習(xí)。
如果你為創(chuàng)業(yè)公司工作,那么這里有一個評估競爭對手的妙招——關(guān)注他們的招聘職位。我讀過大量競爭對手的招聘職位。基本上,這樣就可以大致了解,哪些公司是值得關(guān)注的,哪些是不用在意的。總結(jié):職位描述里使用大量IT詞匯的,內(nèi)容越多,這家公司越構(gòu)不成威脅;要求應(yīng)聘者有Oracle數(shù)據(jù)庫經(jīng)驗的公司,你可以放棄關(guān)注了;招聘C++或Java程序員的公司,對你也不會構(gòu)成威脅;如果招聘Perl或Python程序員,那就需要稍微注意了,他們會存在威脅,至少聽起來,這是一家由黑客控制的技術(shù)公司;如果見到一家招聘Lisp黑客的公司,那么,對手來了。