讓程序員夢寐以求的編程語言,實現編程的完美開發(上)

讓程序員夢寐以求的編程語言,實現編程的完美開發(上)

一心讓臣民行善的暴君可能是最專制的暴君。——C. S. LEWIS(1898—1963,英國小說家)

我的朋友曾對一位著名的操作系統專家說他想要設計一種真正優秀的編程語言。那位專家回答,這是浪費時間,優秀的語言不一定會被市場接受,很可能無人使用,因為語言的流行不取決於它本身。至少,那位專家設計的語言就遭遇到了這種情況。

那麼,語言的流行到底取決於什麼因素呢?流行的語言是否真的值得流行呢?還有必要嘗試設計一種更好的語言嗎?如果有必要的話,怎樣才能做到這一點呢?

為了找到這些問題的答案,我想我們可以觀察黑客,瞭解他們使用什麼語言。編程語言本來就是為了滿足黑客的需要而產生的,當且僅當黑客喜歡一種語言時,這種語言才能成為合格的編程語言,而不是被當作“指稱語義”(denotational semantics)或者編譯器設計。

流行的秘訣

沒錯,大多數人選擇某一種編程語言,不是因為這種語言有什麼獨特的特點,而是因為聽說其他人使用這種語言。但是我認為,外界因素對於編程語言的流行其實沒有想象中那麼大的影響力。我倒是覺得,問題出在對於什麼是優秀編程語言,黑客的看法與大多數的語言設計者不一樣。

黑客的看法其實比語言設計者的更重要。編程語言不是數學定理,而是一種工具,為了便於使用,它們才被設計出來。所以,設計編程語言的時候必須考慮到人類的長處和短處,就像設計鞋子的時候必須符合人類的腳型。如果鞋子穿上去不舒服,無論它的外形多麼優美,多麼像一件藝術品,你也只能把它當作一雙壞鞋。

大多數程序員也許無法分辨語言的好壞。但是,這不代表優秀的編程語言會被埋沒,專家級黑客一眼就能認出它們,並且會拿來使用。雖然他們人數很少,但就是這樣一小群人寫出了人類所有優秀軟件。他們有著巨大的影響力,他們使用什麼語言,其他程序員往往就會跟著使用。老實說,很多時候這種影響力更像是一種命令,對於其他程序員來說,專家級黑客就像自己的老闆或導師,他們說哪種語言好用,自己就會乖乖地跟進。

專家級黑客的看法不是決定一種語言流行程度的唯一因素,某些古老的軟件(Fortran和Cobol的情況)和鋪天蓋地的廣告宣傳(Ada和Java的情況)也會起到作用。但是,我認為從長期來看,專家級黑客的看法是最重要的因素。只要有了達到“臨界數量”(critical mass)的最初用戶和足夠長的時間,一種語言可能就會達到應有的流行程度。而流行本身又會使得這種優秀的語言更加優秀,進一步拉大它與平庸語言之間的好壞差異,因為使用者的反饋總是會導致語言的改進。你可以想一下,所有流行的編程語言從誕生至今的變化有多大。Perl和Fortran是極端的例子,除它們兩個之外,甚至就連Lisp都發生了很大的變化。

所以,即使不考慮語言本身的優秀是否能帶動流行,我想單單流行本身就肯定會使得這種語言變得更好,只有流行才會讓它保持優秀。編程語言的最高境界一直在發展之中。雖然語言的核心功能就像大海的深處,很少有變化,但是函數庫和開發環境之類的東西就像大海的表面,一直在洶湧澎湃。

當然,黑客必須先知道這種語言,才可能去用它。他們怎麼才能知道呢?就是從其他黑客那裡。所以不管怎樣,一開始必須有一群黑客使用這種語言,然後其他人才會知道它。我不知道“一群”的最小數量是多少,多少個黑客才算達到“臨界數量”呢?如果讓我猜,我會說20人。如果一種語言有20個獨立用戶,就意味這20個人是自主決定使用這種語言的,我覺得這就說明這種語言真的有優點。

達到這一步並非易事。如果說用戶數從0到20比從20到1000更困難,我也不會感到驚訝。發展最早的20個用戶的最好方法可能就是使用特洛伊木馬:你讓人們使用一種他們需要的應用程序,這個程序偏巧就是用某種新語言開發的。

外部因素

我們得先承認,確實有一個外部因素會影響到語言的流行。一種語言必須是某一個流行的計算機系統的腳本語言(scripting language),才會變得流行。Fortran和Cobol是早期IBM大型機的腳本語言。C是Unix的腳本語言,後來的Perl和Python也是如此。Tcl是Tk的腳本語言,Visual Basic是Windows的腳本語言,(某種形式的)Lisp是Emacs的腳本語言,PHP是網絡服務器的腳本語言,Java和JavaScript是瀏覽器的腳本語言。

編程語言不是存在於真空之中。“編程”其實是及物動詞,黑客一般都是為某個系統編程,在現實中,編程語言總是與它們依附的系統聯繫在一起的。所以,如果你想設計一種流行的編程語言,就不能只是單純地設計語言本身,還必須為它找到一個依附的系統,而這個系統也必須流行。除非你只想用自己設計的語言取代那個系統現有的腳本語言。

這種情況導致的一個結果就是,無法以一種語言本身的優缺點評判這種語言。另一個結果則是,只有當一種語言是某個系統的腳本語言時,它才能真正成為編程語言。如果你對此很吃驚,覺得不公平,那麼我會跟你說不必大驚小怪。這就好比大家都認為,如果一種編程語言只有語法規則,沒有一個好的實現(implementation),那麼它就不能算完整的編程語言。這些都是很正常很合理的事情,編程語言本來就該如此。

當然,編程語言本來就需要一個好的實現,而且這個實現必須是免費的。商業公司願意出錢購買軟件,但是黑客作為個人不會願意這樣做,而你想讓一種語言成功,恰恰就是需要吸引黑客。

編程語言還需要有一本介紹它的書。這本書應該不厚,文筆流暢,而且包含大量優秀的範例。布賴恩 · 柯尼漢和丹尼斯 · 裡奇合寫的《C程序設計語言》(C Programming Language)就是這方面的典範。眼下,我大概還能再加一句,這一類書籍之中必須有一本由O'Reilly公司出版發行。這正在變成是否能吸引黑客的前提條件了。

編程語言還應該有在線文檔。事實上,在線文檔可以當作一本書來寫,但是目前它還無法取代實體書。實體書並沒有過時,它們讀起來很方便,而且出版社對書籍內容的審核是一種很有用的質量保證機制(雖然做得很不完美)。書店則是程序員發現和學習新語言的最重要的場所之一。

簡潔

假定你的語言已經能夠滿足上面三項條件——一種免費的實現,一本相關書籍,以及語言所依附的計算機系統——那麼還需要做什麼才能使得黑客喜歡上你的語言?

黑客欣賞的一個特點就是簡潔。黑客都是懶人,他們同數學家和現代主義建築師一樣,痛恨任何冗餘的東西或事情。有一個笑話說,黑客動手寫程序之前,至少會在心裡盤算一下哪種語言的打字工作量最小,然後就選擇使用該語言。這個笑話其實與真實情況相差無幾。就算這真的是個笑話,語言的設計者也必須把它當真,按照它的要求設計語言。

簡潔性最重要的方面就是要使得語言更抽象。為了達到這一點,首先你設計的必須是高級語言,然後把它設計得越抽象越好。語言設計者應該總是看著代碼,問自己能不能使用更少的語法單位把它表達出來。如果你有辦法讓許多不同的程序都能更簡短地表達出來,那麼這很可能意味著你發現了一種很有用的新抽象方法。

不要覺得為用戶著想就是讓他們使用像英語一樣又長又囉嗦的語法。這是不正確的做法,Cobol就是因為這個毛病而聲名狼藉。如果你讓黑客像下面這樣求和:

add x to y giving z

而不是寫成:

z=x+y

那麼你就是在侮辱黑客的智商,或者自己作孽了。

簡潔性是靜態類型語言的力所不及之處。不考慮其他因素時,沒人願意在程序的頭部寫上一大堆的聲明語句。只要計算機可以自己推斷出來的事情,都應該讓計算機自己去推斷。舉例來說,hello-world本應該是一個很簡單的程序,但是在Java語言中卻要寫上一大堆東西,這本身就差不多可以說明Java語言設計得有問題了。①

① hello-world程序的唯一作用就是顯示出“Hello, world!”這句話。使用Java語言,你需要這樣寫:

 public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}

如果你從來沒有接觸過編程,看到上面的代碼可能會很奇怪,讓計算機顯示一句話為什麼要搞得這麼複雜?有意思的是,資深程序員的反應與你一樣。

單個的語法單位也應該很簡短。Perl和Common Lisp在這方面是兩個不同的極端。Perl的語法單位很短,導致它的代碼可以擁擠得讓人無法理解,而Common Lisp內置運算符的名稱則長得可笑。Common Lisp的設計者們可能覺得文本編輯器會幫助用戶自動填寫運算符的長名稱。但是這樣做的代價不僅是增加了打字的工作量,還包括提高了閱讀代碼的難度,以及佔用了更多的顯示器空間。

可編程性(Hackability)

對黑客來說,選擇編程語言的時候,還有一個因素比簡潔更重要,那就是這種語言必須能夠幫助自己做到想做的事。在編程語言的歷史上,防止程序員做出“錯誤”舉動的措施多得驚人。這是語言設計者很自以為是的危險舉動,他們怎麼知道程序員該做什麼不該做什麼?我認為,語言設計者應該假定他們的目標用戶是一個天才,會做出各種他們無法預知的舉動,而不是假定目標用戶是一個笨手笨腳的傻瓜,需要別人的保護才不會傷到自己。如果用戶真的是傻瓜,不管你怎麼保護他,他還是會搬起石頭砸自己的腳。你也許能夠阻止他引用另一個模塊中的變量,但是你沒法防止他日日夜夜不知疲倦地寫出結構混亂的程序去解決完全錯誤的問題。

優秀程序員經常想做一些既危險又令人惱火的事情。所謂“令人惱火”,我指的是他們會突破設計者提供給用戶的外部語義層,試著控制某些高級抽象的語言內部接口。比如,黑客喜歡破解,而破解就意味著深入內部,揣測原始設計者的意圖。

你應該敞開胸懷,歡迎這種揣測

。對於製造工具的人來說,總是會有用戶以違揹你本意的方式使用你的工具。如果你製造的是編程語言這樣高度組合的系統,那就更是如此了。許多黑客會用你做夢也想不到的方式改動你的語法模型。我的建議就是,讓他們這樣幹吧,而且應該為他們創造便利,儘可能多地把語言的內部暴露在他們面前。

其實,黑客並不會徹底顛覆你的工具,在一個大型程序中,他可能只是對語言改造一兩個地方。但是,改動多少地方並不重要,重要的是他能夠對語言進行改動。這可能不僅有助於解決一些特殊的問題,還會讓黑客覺得很好玩。黑客改造語言的樂趣就好比外科醫生擺弄病人內臟的樂趣,或者青少年喜歡用手擠破青春痘的那種感覺。②至少對男生來說,某些類型的破壞非常刺激。針對青年男性讀者的Maxim雜誌每年出版一本特輯,裡面一半是美女照片,另一半是各種嚴重事故的現場照片。這本雜誌非常清楚它的讀者想看什麼。

② 在《神經外科醫生手記》(When the Air Hits Your Brain)一書中,神經外科醫生弗託塞克講述了住院總醫生戈雷的一段話,內容關於外科醫生與內科醫生的區別。

戈雷和我要了一個大比薩,找了一張空桌子坐下。他點起一根香菸,說:“那些內科醫生真是令人討厭,總是喜歡談論一輩子只能遇到一次的病例。這就是他們的問題,他們只喜歡古怪的東西,討厭普通的常見病例。這就是我們和他們的區別。你看,我們喜歡腰椎間盤突出,覺得像比薩一樣又大又好吃,但是他們看到高血壓就憎恨不已……”

很難把腰椎間盤突出與又大又好吃聯繫在一起,但是,我想我知道他們指的是什麼。我經常覺得某個bug非常誘人,一定要追蹤下去。不是程序員的人很難想象bug有什麼好玩的。一切正常當然很好,但是不可否認,能夠抓到某些bug會讓人興奮到極點。

一種真正優秀的編程語言應該既整潔又混亂。“整潔”的意思是設計得很清楚, 內核由數量不多的運算符構成,這些運算符易於理解,每一個都有很完整的獨立用途。“混亂”的意思是它允許黑客以自己的方式使用。C語言就是這樣的例子,早期的Lisp語言也是如此。真正的黑客語言總是稍微帶一點放縱不羈、不服管教的個性。

優秀的編程語言所具備的功能,應該會使得言必稱“軟件工程”的人感到非常不滿、頻頻搖頭。與黑客語言形成鮮明對照的就是像Pascal那樣的語言,它是井然有序的模範,非常適合教學,但是除此之外就沒有很大用處了。

一次性程序

為了吸引黑客,一種編程語言必須善於完成黑客想要完成的各種任務。這意味著它必須很適合開發一次性程序。這一點可能出乎很多人的意料。

所謂一次性程序,就是指為了完成某些很簡單的臨時性任務而在很短時間內寫出來的程序。比如,自動完成某些系統管理任務的程序,或者(為了某項模擬任務)自動生成測試數據的程序,以及在不同格式之間轉化數據的程序等。令人吃驚的是,一次性程序往往不是真的只用一次,就像二戰期間很多美國大學造的一大批臨時建築後來都成了永久建築。許多一次性程序後來也都變成了正式的程序,具備了正式的功能和外部用戶。

我有一種預感,最優秀的那些大型程序就是這樣發展起來的,而不是像胡佛水壩那樣從一開始就作為大型工程來設計。一下子從無到有做出一個大項目是很恐怖的一件事。當人們接手一個巨型項目時,很容易被它搞得一蹶不振。最後,要麼是項目陷入僵局,要麼是做出來一個規模小、性能差的東西。你想造一片鬧市,卻只做出一家商場;你想建一個羅馬,卻只造出一個巴西利亞;你想發明C語言,卻只開發出Ada。

開發大型程序的另一個方法就是從一次性程序開始,然後不斷地改進。這種方法比較不會讓人望而生畏,程序在不斷的開發之中逐漸進步。一般來說,使用這種方法開發程序,一開始用什麼編程語言,就會一直用到最後,因為除非有外部政治因素的干預,程序員很少會中途更換編程語言。所以,我們就有了一個看似矛盾的結論:如果你想設計一種適合開發大型項目的編程語言,就必須使得這種語言也適合開發一次性程序,因為大型項目就是從一次性程序演變而來的。

Perl就是一個鮮明的例子。它不僅僅設計成適合開發一次性程序,而且它本身就很像一次性程序。最初的Perl只是好幾個生成表格的工具收集在一起而已。後來程序員用它寫一次性程序,當那些程序逐漸發展壯大後,Perl才隨之發展成了一種正式的編程語言。到了Perl 5,這種語言才適合開發重要的程序,但是在此之前它已經廣為流行了。

什麼樣的語言適合寫一次性程序?首先,它必須很容易裝備。一次性程序是你只想在一小時內寫出來的程序,所以它不應該耗費很多時間安裝和配置,最好已經安裝在你的電腦上了。它必須是想用就用的。C語言可以想用就用,因為它是操作系統的一部分;Perl可以想用就用,因為它本來就是一種系統管理工具,操作系統已經默認安裝它了。

很容易裝備不僅僅指很容易安裝或者已經安裝,還指很容易與使用者互動。一種有命令行界面、可以實時反饋的語言就具有互動性,那些必須先編譯後使用的語言就不具備互動性。受歡迎的編程語言應該是前者,具有良好的互動性,可以快速得到運行結果。

一次性程序的另一個特點就是簡潔。對黑客來說,這一點永遠有吸引力。如果考慮到你最多隻打算在這個程序上耗費一個小時,這一點就更重要了。

函數庫

簡性的最高形式當然是有人已經幫你把程序寫好,你只要運行就可以了。函數庫就是別人幫你寫好的程序,所以它是編程語言的另一個重要特點,並且我認為正在變得越來越重要。Perl就贏在它具有操作字符串的巨大函數庫。這類函數庫對一次性程序特別重要,因為開發一次性程序的原始目的往往就是轉化或提取字符串。許多Perl程序的原型可能就是把幾個函數庫調用放在一起。

我認為,未來50年中,編程語言的進步很大一部分與函數庫有關。未來的函數庫將像語言內核一樣精心設計。優秀函數庫的重要性將超過語言本身。某種語言到底是靜態類型還是動態類型、是面向對象還是函數式編程,這些都不如函數庫重要。那些習慣用變量類型考慮問題的語言設計者可能會對這種趨勢感到不寒而慄。這不等於把語言設計降到開發應用程序的層次嗎?哦,真是太糟了。但是別忘了,編程語言是供程序員使用的,而函數庫就是程序員需要的東西。

設計優秀的函數庫是很難的,並不只是寫一大堆代碼而已。一旦函數庫數量變得太多,找到一個你需要的函數有時候還不如自己動手寫來得快。函數庫的設計基礎與語言內核一樣,都是一個小規模的正交集合。函數庫的使用應該符合程序員的直覺,讓他可以猜得出哪個函數能滿足自己的需要。

(未完待續)

——本文節選自《黑客與畫家:硅谷創業之父Paul Graham文集》[美]Paul Graham 著

讓程序員夢寐以求的編程語言,實現編程的完美開發(上)


分享到:


相關文章: