天天看點

Go語言之父談Go:大道至簡

<b> </b>

摘要:導讀:這篇文章是Google首席工程師、Go語言之父Rob Pike自己整理的6月21日在舊金山給Go SF的演講稿。Rob提到:Go語言本是以C為原型,以C++為目标設計,但最終卻大相徑庭。值得一提的是,這3門語言都曾當選TIOBE年度語言。

導讀:這篇文章是Google首席工程師、Go語言之父Rob Pike自己整理的6月21日在舊金山給Go SF的演講稿。Rob提到:Go語言本是以C為原型,以C++為目标設計,但最終卻大相徑庭。值得一提的是,這3門語言都曾當選TIOBE年度語言。

幾個禮拜之前我被問到:“對于Go語言,最令你驚訝的是什麼?”當時我就明确地給出了答案:“雖然我希望C++程式員能夠使用Go作為替代拼,但實際上大部分Go程式員都是從Python和Ruby轉過來的,其中卻少有C++程式員。”

我、Ken以及Robert都曾是C++程式員,在我們編寫軟體時覺得應該設計一門更适合解決這個問題的程式設計語言。奇怪的是,其他程式員似乎卻不關心。

今天我将說說是什麼讓我們決定創造Go語言的,及其出乎意料的結果。這裡我談的更多的會是Go而不是C++,是以即使你不懂C++也沒關系。

主旨可以簡單地總結為:你更同意Less is more還是Less is less?

這裡有一個真實的故事。貝爾實驗室中心本來配置設定3位數字作代号:實體搜尋是111,計算科學搜尋是127,以及等等。20世紀80年代,随着對搜尋的進一步了解,我們認為有必要新增數位以更好地特征化工作,于是我們的中心就成了1127。

Ron Hardin半開玩笑半正經地說如果我們真的更好地了解了世界,我們已經可以去掉一個數位,把127變成27。當然,管理人員并沒有相信這個玩笑,當然Ron也沒指望他們會相信,但我認為這很有哲理。有時候少意味着更多,你了解得越深,就能越幹練。

請記住這個想法。

回想2007年9月的時候,我正在為Google龐大的C++程式做一些比較瑣碎但是核心的工作(對,就是你們都用過的那個!),我工作在Google龐大的分布式編譯叢集上都需要花45分鐘。之後有幾個C++标準委員會的Google員工為我們做了個演講,他們給我們介紹了下C++0x(現在被叫做C++11)裡會有什麼新東西。

在那一個小時的演講中,我們大概聽到了約35種計劃中的新特性。當然實際上還有更多,但是在那次演講中我們隻聽到了35種左右。有的特性比較小,當然演講中所提到的任何一種特性都可以稱得上标志性的:有的比較精妙,但是很難了解,像右值引用(RValue Reference);其它的很有C++的特色,例如variadic模闆;還有一些隻是一些相當瘋狂的,一如使用者定義文字(user-defined literal)。

這時候,我問了自己一個問題:C++委員會真的認為C++的特性還不夠多?當然,不同于Ron的玩笑,簡化這門語言必是一門更大的成就!也許這很可笑,但是請把這個想法記在心裡。

在那個C++演講的幾個月之前,我做了一個演講(你可以在​​YouTube​​上看到),是關于一個我在上世紀80年代開發的玩具性的并發性語言。那個語言叫作Newsqueak,也就是Go的前身。

我做那個演講是因為我在Google工作的時候忘記了一些Newsqueak裡的好想法,現在回顧一下。我确信它能簡化伺服器端代碼編寫,而且對Google也有好處。

實際上我也試過把這個想法加入C++,但是失敗了。把并發操作和C++控制結構整合到一起非常困難,反過來也意味着即使僥幸成功收益也會非常小。

不過C++0x的演講讓我又重新思考了一遍。新的C++記憶體模型使用了原子類型,這讓我很困擾,我認為Ken和Robert肯定也不喜歡。感覺把這樣一個如此細節的功能加入已經超負載的類型系統裡并不是一個明智的選擇。這看起來也很目光短淺,因為硬體也可能在接下來的十年裡發生明顯的變化,是以把語言和當今的硬體捆綁地太緊不見得是一個正确的選擇。

演講結束後,我們又回到了辦公室。我又開始了編譯工作,我轉過椅子好面向Robert,然後開始問一些關鍵性的問題。在編譯結束之前,我們說服了Ken,決定一起做一些事情。我們再也不想用C++了,同時也很希望能在寫Google的代碼時能用到并行性特性。“大代碼”問題也是一個需要關注的問題,但我們最終決定這個問題最後再解決。

然後我們找了塊白闆,在上面寫下希望能有哪些功能。這裡我們隻關注大的方面,文法和語義學這些細節性的東西先放到一邊去。

之後我們在郵件上交流,這裡是内容摘選:

<col>

Robert:以C語言為原型,修補部分明顯的缺陷,去掉垃圾功能,添加一些缺失的功能。

Rob:命名為“Go”,好處有很多,這名字非常簡短,容易拼寫。工具可以叫做:goc、gol、goa。如果有可互動的調試器/解釋器也可以簡單地叫它“Go”,字尾名用 .go。

Robert:定義空接口文法:interface{}。所有接口都通過這個實作,是以可以用它代替void*。

我們沒有一開始就設計出所有的功能,之後花了一年的時間才确定了array和slice功能,不過也有很多語言的特色在最初幾天就已經确定下來的。

注意:Robert說要以C語言為原型,而不是C++!但實際上我們也并不是真的從C開始,隻是從中借了部分内容,比如運算符、括号和幾個相同的關鍵字。當然為了讓它成為最适合我們的語言,我們從所有了解的語言裡都借取了一些特性。

最後結果毫無疑問跟C或C++大不相同。以下是Go從C和C++簡化的功能:

規範的文法(不需要符号表來解析)

垃圾回收(獨有)

無頭檔案

明确的依賴

無循環依賴

常量隻能是數字

int和int32是兩種類型

字母大小寫設定可見性(letter case sets visibility)

任何類型(type)都有方法(不是類型)

沒有子類型繼承(不是子類)

包級别初始化以及明确的初始化順序

檔案被編譯到一個包裡

包package-level globals presented in any order

沒有數值類型轉換(常量起輔助作用)

接口隐式實作(沒有“implement”聲明)

嵌入(不會提升到超類)

方法按照函數聲明(沒有特别的位置要求)

方法即函數

接口隻有方法(沒有資料)

方法通過名字比對(而非類型)

沒有構造函數和析構函數

postincrement(如++i)是狀态,不是表達式

沒有preincrement(i++)和predecrement

指派不是表達式

明确指派和函數調用中的計算順序(沒有“sequence point”)

沒有指針運算

記憶體一直以零值初始化

局部變量取值合法

方法中沒有“this”

分段的堆棧

沒有靜态和其它類型的注釋

沒有模闆

沒有異常

内建string、slice和map

數組邊界檢查

因為有這麼多功能的簡化,我相信Go比C和C++更有表現力。Less can be more!

但是也不能全部丢棄。你需要把想法分為子產品,例如:類型如何工作、能夠實際上良好運作的文法以及有助于保證類庫互動的東西。

我們也加入了一下C和C++裡沒有的東西,比如slice和map、複合文本(composite literal)、頂層檔案表達式(這是一個大問題,但經常被忽視)、映射(reflection)、垃圾回收等等,我們還非常自然地加入了并發性。

很明顯還缺少了類型層次結構,這讓我很生氣。

在Go語言的首次啟航中,我得到了一個“我不能在沒有泛型的環境下工作”的回報,這真是一個奇怪的言論。

平心而論,我覺得他可能是因為在C++裡用慣了STL的原因。從字面意思來看,這就是說着寫一個int list或者string map對他來說是一個非常大的負擔。我不會在這種奇怪的問題上花太長的時間,是以這種需要泛型支援的也一樣。

但更重要的是,隻有類型是用來解決這樣的問題的,既不是多态函數(polymorphic function)也不是語言原語(language primitive)或者其它類型的幫助,隻有類型!

這就是卡住我的的細節。

從C++和Java轉向Go的程式員很懷念使用類型程式設計的方式,特别是繼承和子類這樣的概念。也許關于“類型”我隻是一個門外漢,但我真沒發現模闆有什麼好處。

我已故的朋友Alain Fournier曾告訴我,他認為學術工作最基本的要求就是分工。你知道嗎?類型層次也隻是一種分類法。你需要決定什麼東西封裝在什麼裡面,每個對象的父類型是什麼?到底是A繼承B還是B繼承A?可排序數組究竟是用來排序的數組還是一個通過數組實作的排序器?如果你堅信類型決定設計那這個就是必須理清的問題!

我認為這是一個對于程式設計的荒謬的思考方式。真正重要的是這些類能為你做什麼而不是它們之間是什麼關系!

當然這就是Go語言裡接口的關系,不過這隻是部分規則,Go語言的設計理念實際上比這還要複雜得多。

如果說C++和Java是關于類的層次和分類,那麼Go的核心思想就是組合(composition)。

Doug McIlroy,Unix管道原理的最終發明者,在1964年寫道:

當我們需要将資料以另一種方式整合到一起時,我們需要一些類似花園裡軟管的方法将程式耦合在一起,這也和IO的處理方式一樣。

同樣Go也是這樣——Go取自這一思想,但是更進一步——它就是一個組合與聯結的語言。

舉個例子:接口提供了組合的方式,隻要實作了方法M就可以直接放在那兒,無論它是什麼都會很合适。

另一個例子是關于并發性如何給我們帶來獨立執行計算能力。

Go有一個非常不同尋常但是很簡單的類型組成方式:嵌入。這些組合技術正是Go的獨特之處,與C++或者Java程式截然不同。

雖然這與Go語言的設計不是很相關,但我還是想說:Go是為大型程式設計的,需要大的團隊編寫和維護。

大型程式設計,通常被認為是C++和Java的領域。我認為,這隻是一個曆史遺留問題,或許還是一個産業事故,但普遍被認為這和面向對象設計有關。

我不贊同這個觀點,大軟體需要确定的理念,但強依賴管理、整潔的接口、抽象化以及優秀的文檔工具這些更為重要!不幸的是,這些沒一個是C++的強項(不過這點Java明顯就好得多)。

當然,現在還不能确定,因為用Go語言編寫的程式還不夠多,但我相信Go會成為一個非常好的大型程式程式設計語言。時間會證明一切!

現在,回到我們講話的中心:

為什麼Go從頭到尾都是以C++為目标來設計的卻無法吸引到C++程式員?

Joke認為,這可能是因為Go和C++的設計理念相差甚大吧。

C++希望所有解決方案都能很容易得到/使用。我在C++11 FAQ(常見問題)上看到了這句話:

  C++能夠優美地、靈活地并且零成本地表現出抽象事物的能力,相比專門編寫的代碼要高效很多。

這種思考方式和Go的運轉方式并不一樣 。零成本不是目标,至少零CPU成本不是。Go的目标是解放程式員!

Go并沒有包羅萬象,你不要期待它什麼都有,你也無法精确地控制每一個細節。例如:Go沒有RAII,但是你可以使用一個垃圾清理器做替代,雖然你甚至無法使用記憶體釋放函數。

你可以從Go中得到很多易于了解但是強大的工具集來組合出問題的解決方案。它跟别的語言比起來也許沒有那麼快或者精緻,但肯定寫起來更簡單、讀起來更容易,也更易于了解,也許還更安全。

從另一個角度看,Go肯定更加簡化:

Python和Ruby程式員轉向Go是因為他們不需要學更多的關心卻可以獲得更好的性能,甚至還可以使用并發特性。

C++程式員不願意轉向Go是因為他們竭盡全力隻為了對自己的程式的完全掌控,并不希望這樣改變。對于他們,軟體并不僅是完成工作,而是做好工作。

現在的問題在于,如果Go成功了,會颠覆他們的世界觀。

而且我們應該在一開始就注意到:對C++11新特性感興趣的人是不會對一個功能如此簡潔的語言感興趣的,即使它能完成更多的任務。