為什么 C++ 程序員不想改用 Go 語言
下面是我在2012年六月舊金山Go SF會議上的發言。
這是一個私人談話。我不單是對在這坐的Go開發團隊成員說,我要感謝團隊在推動Go發展上所做的一切。我還想感謝Go SF組織者給了我跟大家交流的機會。
幾個星期前我被問道這個問題:“你被鼓勵轉到Go后遇到最大的驚喜是什么?”。我立刻知道了答案:雖然我們預期C++程序員會將Go當做一個替代者,然而轉到Go的程序員更多來自于如Python和Ruby等語言,很少有來自C++。
Ken、Robert和我,當我們還是C++程序員時我們設計了一種新的語言來解決我們認為需要用這門新語言來解決的問題。這好像是自相矛盾的,其他C++程序員并不在乎。
今天我想來說說是什么激發我們創建 Go 語言,以及為什么得出這樣的結論應該是意料之中的事情。我承諾這將是一段更傾向于 Go 語言而不是 C++ 語言的發言,所以即使你不了解 C++ 語言,你也可以從這篇文章中了解一二。
實際上,這篇文章的結論可以概括如下:你認為 less is more (少即多),還是 less is less(少即少)?
這里我用一個真實的案例作為比喻。Bell 實驗室中心原來是用 3 位數字來命名的:111 是物理研究所、127 是計算科學研究所等等。在 20 世紀 80 年代,隨著我們對研究的深入了解,已經不足以用 3 位數字來簡單地命名研究所了。所以,我們的研究中心編號變成了 1127 。 Ron Hardin 半開玩笑地說如果我們真的了解了這個世界,我們可以去掉編號前面的 1 位,從 127 變為 27。當然,管理層不會采納這個玩笑,也并沒有被期望他們這么做。但是,我認為,Ron 的說法是有道理的。 Less can be more (精簡反而有更深的內涵)。你了解得越深入,你就能夠用更精煉的語言來概括。
記住這個道理。
回到 2007 年 9 月,我正在做一些為一個大型的 Google C++ 項目做一些瑣碎而中心的工作。你可能接觸過這樣的程序,在我們大型分布式編譯集群上編譯這個程序就用了大概 45 分鐘。后來,聽說了在 C++ 標準委員會的 Google 工程師將會進行 C++0x(即現在的 C++11)新特性的演講。
在這場 1 個小時的演講里,我們聽說了 C++0x 計劃中的 35 項新特性。實際上,或許還有更多的,但演講中只描述了其中的 35 項。當然,一些新特性可能很微小,但是演講中的特性卻都十分重要。一些特性非常精妙而難以理解,像右值引用(rvalue-reference);但同時也有很多 C++ 特色語法,像可變參數模板(variadic templates);而其他一些則非常瘋狂,像用戶定義數據標志(user-defined literals)此刻我問了自己一個問題:C++委員會真的相信C++因為沒有足夠的特性而出問題嗎?當然,作為Ron Hardin玩笑話的變種,簡化編程語言相比于增加語言是更大的進步。這想法是有些可笑,但先記住它。
在那次C++演講前幾個月,我曾給自己一個演講,你可以去油Tube上看,內容是關于我在1980年間創造的一個娛樂性質的并發語言。那個語言叫做Newsqueak,當然它是Go的前驅。
我做那個演講是因為Newsqueak中的一些想法是我在Google工作時錯過的,而我又重新對這些想法進行了思考。我確信它們將使得寫服務器代碼更加簡單,而且Google將會從中受益。
事實上我曾經嘗試去尋找一個方法把這些想法引入C++,但是失敗了。把并發操作和C++的控制結構連接起來太難了,反過來這樣也很難看到真正的優勢。再加上C++讓自己看起來非常難處理,盡管我承認很難使語言真正易用。所以我遺棄了這個想法。
但C++0x演講讓我重新思考。真正困擾我的——我認為也困擾著Ken和Robert——是具有原子類型的新C++內存模型。在一個已經超負荷的類型系統中加入這樣一個定義微觀細節的集合,讓人感覺是錯的。這看起來也非常短時,因為硬件很可能在下一個十年里有明顯的變化,把語言和今天的硬件結合這么緊密將是不明智的。
會議結束后,我們回到辦公室。繼續開始了新一輪的編譯,然后轉過桌子面對著 Robert,開始詢問那些關注的問題。在編譯結束以前,我們說服了 Ken ,決定自己動手做點什么。我們并不希望一直用 C++ 寫程序,我們,特別是我,希望在為 Google 寫代碼的時候能夠自然地使用并發編程技術。我們也希望強調“Programming in large”的方向。
我們在白板上寫了很多我們想要的特性。我們從大方向出發,忽略詳細的語法和語義只關注事物的主要部分。
我還保留著在那周里一個郵件列表,下面是一些節選:
Robert:起點: C語言,解決一些明顯的瑕疵、刪除雜質、增加一些缺少的特性。
Rob:命名為 Go 語言吧,你可以為這個名字想一些原因。但是它已經有不少美好的屬性了。簡單地說,易于書寫。工具命名也容易:goc、gol、goa。如果有一個交互的調試器、解釋器的話,它可以就叫“go”。文件名后綴就是“.go”。
Robert:空接口:interface {}。可以被所有接口實現,所以,這個接口可以代替 void* 的位置。
這時候,我們并沒有完全弄清楚這種語言。舉例來說,我們用了大概一年時間才弄清楚 array 和 slice 的結構。但是在最初的那幾天,很多重要的特性就已經出現了。
注意到 Robert 說 C 語言是起始點,而不是 C++ 語言。我不確定,但我相信他指的是 C 語言,因為當時 Ken 也在。但是實際上,最后我們并不是真的從 C 語言開始擴展。我們完全重新開始了這門語言,稍微從 C 語言借鑒了操作符、括號以及一些普遍的關鍵字。(當然,我們也從其他我們所知道的語言中借鑒了很多想法。)在任何情況下,在我看來,我們從基礎上、打破一切的思路以及重新開始來回應 C++ 語言。我們并不是嘗試設計一個更好的 C++ 、甚至說一個更好的 C 語言。它只是一個對我們所關注的軟件更好的語言。
最后,它當然和 C 或者 C++ 都相當不同。不同點甚至比大家注意到的多。我列舉一下 Go 語言對 C/C++ 語言的關鍵的簡化:
- 正則語法(不需要一個符號表來做語法分析)
- 垃圾回收
- 沒有頭文件
- 沒有循環依賴
- 常量只是數字
- int 和 int32 是兩種不同的類型
- 用大小寫字母區別變量的可見性
- 對所有類型可用的方法(沒有類的語法)
- 沒有子類型繼承(沒有子類)
- 包層次的初始化和良好定義的初始化順序
- 文件的編譯以包組織
- 任意順序出現的包層次全局變量
- 沒有算術轉換(arithmetic conversions)(通過常量支持)
- 接口是隱含的(不需要用“implements”顯式聲明)
- 嵌入的(沒有超類的好處)
- 方法被聲明為函數(不需要特殊的地方)
- 方法就是函數
- 接口是沒有數據的方法
- 方法以參數名匹配,而不是通過類型
- 沒有構造函數或者析構函數
- 后增量運算(x++)和后減量運算(x--)是語句而不是表達式
- 沒有前增量運算和前減量運算
- 賦值操作不是表達式
- 沒有序列點(sequence point)的概念:evaluation order defined in assignment, function call
- 沒有指針運算
- 內存分配總是初始化為 0
- 可以獲取本地變量的地址
- 在方法中沒有 this 關鍵字
- 分段的棧結構
- 沒有 const 或者其他類型注解
- 沒有模板結構
- 沒有異常處理結構
- string、slice 和 map 是語言結構
- 數組越界檢查
盡管需要精簡以及缺失部分的列表依然很長,但我相信,Go 比 C 或 C++ 更具表現力。積少可以成多。
當然,沒有人可以一下子拿出所有的東西。諸如類型行為、實踐中運行良好的語法以及使庫間相互運行良好的不可言喻的部分等等需要慢慢積累。
我們也添加了一些 C 或 C++ 中所沒有的東西,例如 slices 和 maps、復合聲明,頂級文件表達式(非常龐大,且多數部分尚不為人知),反射,拉圾回收等。當然,還有原生的并發。
明顯缺失的一項是類型層次。這里我可以說臟話么?
早在GO語言第一次出現的時候,就有人說,不能想象在工作中如何使用一個沒有泛型的編程語言。這是我在某處作報告的時候,知道了這種奇怪的想法。
公平的說,他只是以自己的立場發表看法,或許他真的很喜歡C++中的STL為他帶來的便利。所以為了討論的目的,我們只有把他的想法看成很膚淺表面的東西。
按照那種想法,編寫一個容器,例如整型的list和字符串的map,是一種不能承受的工作量。我知道了這種奇怪的想法后,花了我很少很少的編程時間實現了這些容器,即使是使用一些沒有泛型的編程語言。
但更重要的是,那種想法中把類型作為解決這一難題的方法。類型,不是多態函數,不是語言基元不是其他方法的輔助,竟然是類型。
那是與我相關的細節。
從C++和Java轉向Go的程序員不懂得使用類型編程的想法,尤其是繼承、子類和與之相關的內容。也許關于類型我是個門外漢,但我還沒發現一個模型能有如此的表現力。
我的一位老友Alain Fournier曾對我說,他把學術工作的最低層次看作是分類學。你知道嗎?類型分層恰恰就是分類學。你需要決定哪塊進什么箱子,每個類型的父類,無論是 A繼承于B還是B繼承于A。一個可排序的數組是一個排序數組或者是一個用分類機表示的數組嗎?如果你相信類型地址對這些問題都進行了設計,那你就必須對這個問題做出判斷。
我認為那是個可笑的方法去思考編程。重要的不是事物間的祖先關系,而是它們可以為你做什么。
當然,那就是進入Go的接口所在。但它們只是更大圖景,即真正的Go哲學的一部分。
如果C++和Java是關于類型層次和類型分類的話,那Go是關于構造。
Doug McIlroy, Unix管道的發明者,在1964(!)中寫道:
我們應該使用一些方法,像花園澆水軟管一樣來連接程序——當需要用另一種方法處理數據時,猶如擰水管一樣進入另一個分支。這也是IO的方式。這也是Go的方式。Go接受了這個想法并發揚光大。它是一個構造和耦合的語言。
很明顯的例子是它提供給我們構造成分的接口方式。成分是什么不重要,如果它實現了方法M,我可以把它放在這里。
另一個重要的例子是并發性如何構造那些獨立執行的計算。
甚至有一個非尋常(非常簡單)的類型構造形式:嵌入。
這些構造技術正是Go的風味特色,這是和C++或者Java程序的最大的區別。
===========
我想提一下Go設計中的一個不相干的方面:Go是為了寫大型程序、被大型團隊編寫并維護而設計的。
關于“大型編程”的想法,從某種角度看C++和Java占據著這個領域。我認為這僅僅是個歷史偶然現象,或者是個業界偶然現象。但大部分人都覺得這和面向對象設計有某種關系。
我一點也不相信這些。大型軟件當然需要方法論,但更加需要的是強依賴性的管理、干凈的接口抽象和優秀的文檔工具,而這些C++都不能很好的處理(盡管Java明顯好些)。
我們現在還不知道,因為還沒有足夠的用Go寫的軟件,但我相信Go將證明是一個優秀的用于大型編程的語言。時間將證明一切。
現在是時候回答我們剛開始提出的令人驚訝的問題:
為什么Go,一種完全為C++是怎樣用所設計的語言,又不吸引更多的C++程序員?
玩笑撇在一邊,我認為其原因是Go和C++有著完全不同的哲學。
C++是可以讓你在指尖擁有一切。我在C++11FAQ中發現了如下引用:
C++抽象范圍可表示的很優雅,靈活,手工制作的專業代碼和零成本相比是大大增加的。
這樣的想法和Go的操作方式是不同的,零成本可不是Go的目標,但至少不是CPU零成本。Go 宣稱極小化編程人員的付出是它們認為最重要的。
Go 語言不是包羅萬象的。Go 并不自帶很多函數,不會對每一個執行細節都有精確的控制。例如,你不會有 RAII。相反地,Go 語言有一個垃圾收集器(Garbage Collector,即GC),因此,不需要一個釋放內存的函數。
Go 語言給你的是一套強大且易于理解、易于構建的對問題解決方案。它不是一種像你用其他語言一樣的快而復雜、或者說思想上有所激發的語言,但它幾乎肯定會更容易編寫、更易于閱讀、更容易理解、更容易維護,也許還更安全。
用另一種方式來說,當然過于簡潔:
Python和Ruby程序員轉向Go,因為他們不需要屈服于大量的表達形式,但可以提升性能,并能使用并發性。
C++程序員沒有轉向Go,因為他們為精確控制他們的編程領域付出了很多,不想屈服與它的任一方面。對他們而言,軟件并不僅僅意味著搞定工作,它意味著使用某種確定的方法去做。
那么,問題是Go的成功將與他們的世界觀沖突。
我們本應該從開始時就意識到這一點。那些因為C++11的新特性而激動不已的人是不會關心一個有更少特性的語言。即使,最終它能提供更多。
謝謝。
本文地址:http://www.oschina.net/translate/less-is-exponentially-more
原文地址:http://commandcenter.blogspot.it/2012/06/less-is-exponentially-more.html