ESR:程序語言設計的要詣和真諦
當你真正掌握了整體化的工程設計思維時,你就會發現高屋建瓴的工程設計已經遠遠超越了技術優化的層面。我們的每一件創造都催生於人類活動的大背景下,被這種人類活動賦予了廣泛的經濟學意義、社會學意義,甚至於具有了奧地利經濟學家所稱的「 人類行為學意義 」。而這種人類行為學意義則是明確的人類行為所能達到的最高層次。
對我來說這並不只是一種抽象的理論。當我在撰寫關於開源項目開發的文章時,文章的內容正是關於人類行為學的 —— 這些文章並不涉及哪個具體的軟體技術或者話題,而是在討論科技所服務的人類行為。從人類行為學角度對科技進行更深入的理解,可以幫助我們重塑科技,並且提升我們的生產力和成就感。這種提升並不總是因為我們有了更新的工具,而更多的是因為我們改變了使用現有工具的思路,提升了我們對這些工具的駕馭能力。
在這個思路之下,我的隨筆文章的第三篇中談到了 C 語言的衰退和正在到來的巨大改變,而我們也確實能夠感受到系統級別編程的新時代的到來。在這裡,我會把我的統觀見解總結成更具體的、更實用的對計算機語言設計的分析。例如總結出為什麼一些語言會成功,另一些語言會失敗。
在我最近的一篇文章中,我寫道:所有計算機語言的設計都是對機器資源和程序員人力成本的相對權衡的結果;是對一種相對價值主張的體現。而這些設計思路都是在硬體算力成本不斷下降,程序員人力成本相對穩定且可能不減反增的背景下產生的。我還強調了語言設計在實現了一些原有的權衡方案之後,其未來的轉化和演變成本在這種語言的成敗中所要扮演的一些額外角色。在文中我也闡述了編程語言設計者是如何為當前和可見的未來尋找新的最優設計方案的。
現在我要集中講解一下我在上面段落里最後提到的那個概念,即語言設計工程師們其實可以在多個方面來改進和提高現階段編程語言的設計水準。比如輸入系統的優化,GC (垃圾回收機制) 和手動內存分配的權衡,命令導向、函數導向和面向對象導向的混合和權衡。但是站在人類行為學的角度去考量,我認為設計師們一定會做出更簡單的設計權衡,即針對近景問題還是針對遠景問題來優化一種語言的設計。
所謂的「遠」、「近」之分,是指隨著硬體成本的逐漸降低,軟體複雜程度的上升和由現有語言向其他語言轉化的成本的增加,根據這些因素的變化曲線所做出的判斷。近景問題是編程人員眼下看到的問題,遠景問題則是指可預見的,但未必會很快到來的一系列情況。針對近景問題的解決方案可以被很快部署下去,且能夠在短期內非常有效,但隨著情況的變化和時間的推移,這種方案可能很快就不適用了。而遠景的解決方案可能因為其自身的複雜和超前性而夭折,或是因其代價過高無法被接受和採納。
在計算機剛剛面世的時候, FORTRAN 就是一個近景設計方案, LISP 語言的設計則是針對遠景問題;彙編語言多是近景設計方案,很好的闡明了這類設計很適用於非通用語言,同樣的例子還包括 ROFF 標記語言。PHP 和 Javascript 則是我們後來看到的採用了近景設計思維的語言。那麼後來的遠景設計方案有哪些例子呢? Oberon、Ocaml、ML、XML-Docbook 都是它的例子。學術研究中設計出的語言多傾向於遠景設計,因為在學術研究領域,原創性以及大膽的假設與想法是很重要的。這和學術研究的動機以及其獎勵機制很有關係(值得注意的是,在學術研究中,傾向於遠景設計的本源動機並非出於技術方面的原因,而是出自於人類行為,即標新立異才能在學術上有成績)。學術研究中設計出的編程語言是註定會失敗的;因為學術研究的產物常常有高昂的轉入成本,無人問津的設計。這類語言也因為在社區中不夠流行而不能得到主流的採納,具有孤立性,且常常被擱置成為半成品。(如上所述的問題正是對 LISP 歷史的精闢總結,而且我是以一個對 LISP 語言有深入研究,並深深喜愛它的使用者的身份來評價 LISP 的)。
一些近景設計的失敗案例則更加慘不忍睹。對這些案例來說,我們能夠期待的最好的結果就是這種語言可以消亡的相對體面一些,被一種新的語言設計取而代之。如果這些近景設計導向的語言沒有死亡而是一直被沿用下去(通常是因為轉化成本過高),那麼我們則會看到不斷有新的特性和功能在這些語言原來的基礎之上堆積起來,以保持它們的可用性和有效性。直到這種堆積把這些語言變得越來越複雜,變的危若累卵且不可理喻。是的,我說的就是 C++ 。當然, 還有 Javascript。Perl 也不例外,儘管它的設計者 Larry Walls 有不錯的設計品味,避免了很多問題,讓這種語言得以存活了很多年。但也正是因為 Larry Walls 的好品味,讓他在最終對 Perl 的固有問題忍無可忍之後發布了全新的 Perl 6。
從這種角度去思考程序語言,我們則可以把語言設計中需要側重的目標重新歸納為兩部分: (1)以時間的遠近為軸,在遠景設計和近景設計之間選取一個符合預期的最佳平衡點;(2)降低由一種或多種語言轉化為這種新語言的轉入成本,這樣就可以更好地吸納其它語言的用戶群。接下來我會講講 C 語言是怎樣佔領全世界的。
在整個計算機發展史中,沒有誰能比 C 語言在選擇遠景和近景設計的平衡點的時候做的更完美。事實勝於雄辯,作為一種實用的主流語言,C 語言有著很長的壽命,它目睹了無數個競爭者的興衰,但它的地位仍舊不可取代。從淘汰它的第一個競爭者到現在已經過了 35 年,但看起來 C 語言的終結仍舊不會到來。
當然,你可以把 C 語言的持久存在歸功於文化惰性,但那是對「文化惰性」這個詞的曲解,C 語言一直得以延續的真正原因是因為目前還沒有人能提供另一種足夠好的語言,可以抵消取代 C 語言所需要的轉化成本!
相反的,C 語言低廉的 內向轉化成本 (轉入成本)並未引起大家應有的重視,C 語言幾乎是唯一的一個極其多樣和強大的編程工具,以至於從它漫長統治時期的初期開始,它就可以適用於多種語言如 FORTRAN、Pascal、彙編語言和 LISP 的編程習慣。在一九八零年代我就注意到,我常常可以根據一個 C 語言新人的編碼風格判斷出他之前在使用什麼語言,這也從另一方面證明了 C 語言可以輕鬆的被其它語言的使用者所接受,並吸引他們加入進來。
C++ 語言同樣勝在它低廉的轉化成本。很快,大部分新興的語言為了降低自身的轉入成本,都紛紛參考了 C 語言的語法。值得注意的是這給未來的語言設計帶來了一種影響:即新語言的設計都在儘可能的向 C 的語法靠攏,以便這種新語言可以有很低的內向轉化成本(轉入成本),使其他語言的使用者可以欣然接受並使用這種新語言。
另一種降低轉入成本的方法則是把一種編程語言設計的極其簡單並容易入手,讓那些即使是沒有編程經驗的人都可以輕鬆學會。但做到這一點並非易事。我認為只有一種語言 —— Python —— 成功的做到了這一點,即通過易用的設計來降低內向轉化成本。對這種程序語言的設計思路我在這裡一帶而過,因為我並不認為一種系統級別的語言可以被設計的像 Python 一樣傻瓜易用,當然我很希望我的這個論斷是錯的。
而今我們已經來到 2017 年末尾,你一定猜測我接下來會向那些 Go 語言的鼓吹者一樣對 Go 大加讚賞一番,然後激怒那些對 Go 不厭其煩的人群。但其實我的觀點恰恰相反,我認為 Go 本身很有可能在許多方面遭遇失敗。Go 團隊太過固執獨斷,即使幾乎整個用戶群體都認為 Go 需要做出某些方面的改變了,Go 團隊也無動於衷,這是個大問題。目前,Go 語言的 GC 延遲問題以及用以平衡延遲而犧牲掉的吞吐量,都可能會嚴重製約這種語言的適用範圍。
即便如此,在 Go 的設計中還是蘊含了一個讓我頗為認同的遠大戰略目標。想要理解這個目標,我們需要回想一下如果想要取代 C 語言,要面臨的短期問題是什麼。正如我之前提到的,這個問題就是,隨著軟體工程項目和系統的不斷擴張,故障率也在持續上升,這其中內存管理方面的故障尤其多,而內存管理故障一直是導致系統崩潰和安全漏洞的主要元兇。
我們現在已經認清,一種語言要想取代 C 語言,它的設計就必須遵循兩個十分重要準則:(1)解決內存管理問題;(2)降低由 C 語言向本語言轉化時所需的轉入成本。從人類行為學的角度來縱觀編程語言的歷史,我們不難發現,作為 C 語言的准替代者,如果不能有效解決轉入成本過高這個問題,那設計者所做的其它部分做得再好都不算數。相反的,如果一種 C 的替代語言把轉入成本過高這個問題解決地很好,即使它在其他部分做的不是最好的,人們也不會對這種語言吹毛求疵。
而 Go 正是遵循了上述兩點設計思路,雖然這個思路並不是一種完美無瑕的設計理論,也有其局限性。比如,目前 GC 延遲的問題就限制了 Go 的推廣。但是 Go 目前選擇了照搬 Unix 下 C 語言的傳染戰略,把其自身設計成一種易於轉入,便於傳播的語言。這樣它的廣泛和快速的傳播就會使其迅速佔領市場,從而打敗那些針對遠景設計的看起來更好的語言。
沒錯,我所指的這個遠景設計方案就是 Rust。而 Rust 的自身定位也正是一種遠景和長期的 C 語言替代方案。我曾經在之前的一些文章中解釋過我為什麼認為 Rust 還沒有做好和 Go 展開競爭的準備。TIBOE 和 PYPL 的語言評價指數榜也很好的證明了我的對於 Rust 的這個觀點。在 TIBOE 上 Rust 從來沒有進過前 20 名。而在 TIBOE 和 PYPL 兩個指數榜上, Rust 都要比 Go 的表現差很多。
五年後的 Rust 會發展的怎樣還未可知。但如果 Rust 社區的開發人員對這種語言的設計抱著認真投入的態度,並願意傾聽,那麼我建議他們要特別重視轉入成本的問題。以我個人經歷來說,目前由 C 語言轉入 Rust 語言的壁壘很高,使人望而卻步。如果 Corrode 之類的 Code-lifting 工具只是把 C 語言映射為不安全的 Rust 語言,那麼 Corrode 這類工具也是不能解決這種轉入壁壘的。或者如果有更簡單的方法能夠自動注釋代碼的所有權或生命周期,那麼編譯器就能把 C 代碼直接映射到 Rust,人們也不再需要 Corrode 這一類工具了。目前我還不知道這個問題要如何解決,但我覺得 Rust 社區最好能夠找到一種解決方案來代替 Corrode 和其同類工具。
在最後我想強調一下,Ken Thompson 曾經有過語言設計的輝煌歷史。他設計的一些語言雖然看起來只是為了解決近景問題,實際上卻具有很高的質量和開放程度,讓這些語言同樣非常適合遠景問題,非常易於被提高和拓展。當然 Unix 也是這樣的, 這讓我不禁暗自揣測,那些我認為的 Go 語言中乍看上去不利於其遠景發展的一些令人擔憂煩擾的設計(例如缺乏泛型)也許並沒有我想像的那樣糟糕。如果確如我所認為的那樣,即這些設計會影響 Go 的遠景發展,那麼恐怕我真的是比 Ken 還要聰明有遠見了。但是我並不認為我有那麼高明。Go 的前途我們還是只能拭目以待。
via: http://esr.ibiblio.org/?p=7745
作者:Eric Raymond 譯者:Valoniakim,yunfengHe 校對:yunfengHe,wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive