• 少即是極多

    0
    C/C++ Go ci 9247 次瀏覽

    網友 @innocentim (Twitter)投稿于酷殼

            這是一篇翻譯練習。力圖保留原意。若有不準確處,求速速指出。猛擊此處(墻)看原文。作者為 Rob Pike,貝爾實驗室來的大牛,現在就職于 Google。他主導了 Go 語言的創建工作。下面是正文——

            ——————————————正文分隔線——————————————

    少即是極多
    這是我在 2012 年 6 月的 Go SF 上演講的文本。

            這是一個個人演講。 我承認,雖然面前的團隊讓 Go 誕生并延續,但是我的觀點并不代表任何其他 Go 語言小組成員的意見。 我也想感謝 Go SF 的組織者提供這個和你們交流的機會。

            幾星期前我被問起:“你在推出 Go 的過程中遇到的最大的驚喜是什么?”我立即意識到了答案: 雖然我們希望 C++ 程序員意識到 Go 是個較好的選擇,但是令人意外的是,大多數 Go 程序員來自 Python 和 Ruby 這樣的動態語言,而很少有來自 C++ 的。

            我們——Ken,Robert 和我——是 C++ 程序員(譯者: Ken 也用C++?),當時在為解決我們所寫的這類軟件產生的問題設計一個新的語言。 這似乎有點自相矛盾,因為別的 C++ 程序員根本不關心這些問題,更不會去設計一個語言。

            我今天想說的是關于那些激發我們創造 Go 的事情,和為什么它本不應令我們如此驚訝。 我保證這些內容更多與 Go 相關而不是C++,所以即使你不很了解 C++ 你也能跟得上。

            回答可以這樣歸結: 你認為”少即是多”呢,還是”少就是少”?

            這里有個比喻,將以真實故事的形式給出。 貝爾實驗室中心原來發放 3 位數號碼: 物理研究是 111,計算科學研究是 127,如此這般。 1980 年代早期,一個便箋飛過來說”鑒于你們對研究的理解有所加深,將為你們的號碼多加上一位,以便更好地體現你們的工作”。 所以我們中心的號碼變成了 1127。 Ron Hardin 半當真地開玩笑說如果我們真的理解我們的世界更好一點的話,我們將丟掉一位數字,將 127 變成 27。 當然主管沒聽到這個笑話(這也不是我們希望的),但是我想這里面有點值得思考的東西。 少即是多。 你理解得越好,你將變得越簡潔。

            先記住這句話。

            回到 2007 年 9 月,我在做一個龐大的 Google C++ 項目的細微但核心的部分。 開發必須交互進行,但是我這部分在我們的 Google 編譯集群上要編譯 45 分鐘。 同時,有個消息傳過來說一群在 C++ 社區的 Google 員工將開一場講座,介紹即將到來的C++0x (現在稱為C++11)。

            在那場持續一小時的講座中,我們聽說了諸如計劃中的 35 個新特性的說法——事實上還有更多,但是那場講座只說有 35 個。 有些特性當然是細微的,但是講座中談到的至少是足夠重要的。 提到的特性中,有些十分微妙并難以理解,比如右值引用(rvalue references); 有些特別符合 C++ 范兒,比如可變參數模板(variadic templates); 還有些十分瘋狂,比如用戶定義的字面量(user-defined literals)。

            那時候我問了自己一個問題: C++ 社區真的覺得 C++ 錯在沒有足夠多的特性么? 顯然,從 Ron Hardin 的笑話的角度看,簡化語言將比添加新特性取得更好的效果。 當然,對 C++ 來說這很不靠譜,但是先記住這點。

            在這場講座的幾個月之前我做了一場講座(你可以通過 YouTube 看到),講的是一個我 1980 年代做的一個玩具并發編程語言。 這個語言叫 Newsqueak,而且顯然地,它成為了 Go 的前身。

            在我在 Google 工作的過程中,我發現我丟掉了 Newsqueak 中的一些點子。 現在我將重新思考它們,所以我才做了那場講座。 我相信它們會讓服務器端編程變得更容易,而且 Google 能真正從中獲益。

            我真的嘗試將這些點子加入到 C++ 中,可惜失敗了。 我實在難以將一組并發操作融入到 C++ 的控制流程中去——當真融進去的話,它們將變得十分丑陋,從而難以看到優越性。 另外,C++將它變得十分臃腫(雖然我從來沒真正發現 C++ 苗條過)。 所以我放棄了這個想法。

            但是C++0x 的講座使我再次思考。 一件事十分困擾我——我相信也困擾著 Ken 和 Robert——C++的新內存模型居然新增了原子類型。 為這個不堪重負的類型系統加上這么個細致精巧到極致類型機制十分的不靠譜,不是么? 將語言和今日的硬件綁在一起似乎有點目光短淺并且不明智,因為硬件過幾年就有大變。

            那場C++0x 講座結束之后,我們回到辦公室。 我開始了另一個編譯(譯者笑),轉過轉過我的椅子,面對 Robert,然后開始問一些尖銳的問題。 在編譯完成之前,我們拉攏了 Ken,并決定做些什么。 我們再也不想寫 C++ 了,并且我們——尤其是我——在寫 Google 代碼時,想讓并發拿來就用。 同時我們也想解決”大系統編程”的問題,容后細說。

            我們在白板上寫下一組我們需要的東西——迫切需要的那種。 我們規劃出大體的輪廓,忽略了語法細節和語義。

            我仍然有一條碉堡了的那周的郵件線索。 這是一些摘錄:

    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和 C++ 的 Go 的重要的簡化的列表:

    • 常規的語法(不需要一個符號表來輔助解析)
    • GC 機制(僅僅是 GC)
    • 沒有頭文件
    • 顯式依賴關系
    • 沒有循環依賴
    • 數字常量僅僅是數字(譯者: 沒有類型)
    • int 和 int32 不是同種類型
    • 字母大小寫將確定可見性
    • 任何類型都可以有方法(沒有類)
    • 沒有子類型繼承(沒有子類)
    • 包級別的初始化和良好定義的初始化順序
    • 同一個包的文件一起編譯
    • 包級別的全局定義可以以任意順序進行
    • 沒有算術類型轉換(常量可以彌補)
    • 接口是隱式實現的(沒有”implements”聲明)
    • 嵌入的結構體(沒有類型提升和子類)
    • 方法像函數一樣定義(不必定義在特殊的地方)
    • 方法就是函數
    • 接口就是方法(沒有數據)
    • 方法僅僅靠名字匹配(不是靠類型)
    • 沒有構造函數和析構函數
    • 后置增量/減量運算符僅僅是語句,而不是表達式
    • 沒有前置增量/減量運算符
    • 賦值號是語句,不是表達式
    • 表達式求值順序在賦值和函數調用時確定(沒有所謂的”sequence point”)
    • 沒有指針算術
    • 內存總是初始化為0
    • 對本地變量取地址是合法的
    • 方法中沒有叫 this 的指針
    • 分段式棧
    • 沒有常量或其它類型的注記
    • 沒有模板
    • 沒有異常
    • 內建字符串,切片和映射(map)
    • 數組邊界檢查

            并且,我相信通過這一系列的簡化,Go 將比C或 C++ 更具有表現力。 少即是多。

            但是我們沒法一下子把所有部分都做出來。 我們需要構建最基礎的部分,比如說類型系統的表示,能良好應用于實際的語法,和一些無法形容的但能讓庫更容易相互操作的東西。

            我們同樣增加了C或 C++ 中沒有的東西,比如切片和映射,組合字面量(?),文件頂層的表達式(這雖是件大事,但是幾乎不為人知),反射機制,GC 等等。 自然,還有并發。

            一個顯眼的缺少的東西是類型的繼承。 請允許我粗暴地對待它一分鐘。

            早先構建 Go 的時候有人跟我說,他無法想象用一門沒有泛型的語言工作。 正如我在別處說明的那樣,我覺得這是個很詭異的言論。

            公平起見,他用自己的話說可能是他真的很喜歡 C++ 中 STL 的那些容器。 以辯論為目的的話,我們來正面看看他的言論。

            他說的意味著: 他發現寫一個容器,比如以 int 為元素類型的鏈表,或字符串映射是一種不能忍的重負。 我發現這是個很詭異的言論,因為我幾乎沒把時間花在那些個問題上,即使我在用沒有泛型的語言。

            但是,更重要的是,他說的那些表示類型系統將會解除這種負擔。 類型系統。 不是多態函數,或語言級原語,或其它類型的輔助手段(helpers),而僅僅是類型系統

            這就是粘住我的那個細節。

            從 C++ 或 Java 來 Go 的程序員懷念和類型系統在一起的日子,特別是帶繼承和子類的那部分。 也許我在類型系統方面是粗暴了些,但是我絕不覺得那套玩意非常具有表現力。

            我已故的朋友 Alain Fournier 一次告訴我說他認為學術工作的最底層是分類學。 然后信不信由你,類型繼承正是分類學。 你必須決定哪個蘿卜扔哪個坑里,每個類型的父類型,A是否繼承B或者B是否繼承A。 一個可排序的數組是一個帶有 sort 方法的數組呢,還是一個長得像數組的排序器呢? 如果你覺得類型系統能解決所有設計上的問題,你必須做出這個無意義的選擇。

            我相信對編程來說那是個荒誕的思路。 真正的重點不在于事物之間的繼承關系,而在于它們能提供些什么。

            因此,接口這個概念進入了 Go。 但是它們都是主要部分——真正的 Go 之道——的一部分。

            如果 C++ 和 Java 注重類型繼承和類型系統的分類學,那末 Go 就注重組合。

            Doug Mcilroy,Unix 管道的最終發明人,在 1964 年(!)寫道:

    我們應該有一些機制能將程序耦合(串)起來,像花園軟管那樣——當我們需要另一種方式傳送數據時,擰緊另外一段即可。 I/O也可以這么做。

            這也是 Go 所提倡的道路。 Go 吸收這個觀點,然后把它推進得十分遠。 這是一門關于(功能上的)組合和(調用上的)耦合的語言。

            一個顯然的例子是接口是組合各部分的途徑。 關鍵是,那些部分是什么并不重要,如果某類型實現了M方法我就可以把這個方法填到接口里去。

            另一個重要的例子是如何讓并發性提供給我們不同的獨立計算部分的組合。

            并且還有一種不同尋常(但十分簡單)的類型組合形式: 嵌入。

            ————————————————————————

            我想提一個和之前不太相關的 Go 設計: Go 被設計為大型團隊用來寫大型程序的語言。

            這里有個概念是”大型編程”,并且不知何故 C++ 和 Java 主宰了這個領域。 我相信這只是因為其歷史巧合,或者是工業上的巧合。 但是被廣泛接納的觀點是他們和面向對象設計有關。

            我壓根不相信這點。 大型軟件需要確定的方法,但是更重要的是它需要強依賴性管理,干凈的接口抽象和優越的文檔工具。 C++ 沒一點做得好的(雖然 Java 明顯要好很多)。

            我們還不知道 Go 語言能做到何種程度,因為現在還沒有足夠的軟件是用 Go 寫的。 但是我非常有信心于 Go 將會成為一個優越的大型編程語言。 時間會說明一切的。

            ————————————————————————

            現在,回到我們演講開始提的那個問題:

            為什么 Go,作為從頭被設計為符合 C++ 使用者習慣的語言,沒有吸引很多 C++ 程序員?

            嚴肅點說,我覺得是因為 Go 和 C++ 在哲學方面有著巨大的不同。

            C++是將所有東西提到你指尖上(譯者: 即多范式)。 我在C++11的 FAQ 上找到了這段引用:

    C++能優雅地,靈活地,零損耗地(相比于手工操縱代碼)表達抽象的能力大幅提升了。

            Go 并非這種”圍繞式”的。 你并不需要所有的東西都內建好。 你不需要對每個執行細節進行精細的控制。 比如,你不需要 RAII,但你擁有一個垃圾回收器,也意味著你不需要執行釋放內存的操作。

            你得到的是一組非常強有力但易于理解,易于用來構建積木的功能,這些積木可以用來組合出一個你需要的問題的解法。 這并不意味著它能像別的一些語言創造的解法一樣快速,復雜,或帶來思想上的激勵,但是它總能保證易于書寫,易于閱讀,易于理解,易于維護,而且可能更安 全。

            從另一個角度說,這當然算作過度簡化:

            Python 和 Ruby 程序員轉到 Go,因為他們不需要犧牲表達能力,卻獲得了性能的提升,并且能好好玩并發系統了。

            C++程序員并沒有轉到 Go 是因為他們好不容易獲得了對程序的精細控制,并且不想犧牲它們的任何一部分。 對他們而言,寫軟件不僅包括把事情做完,而且包括用特定的方式完成。

            關鍵是,在將來,Go 的成功將會顛覆他們的世界觀。

            并且從一開始我們就應該意識到這點。 對于C++11的新特性很興奮的人們并不關心一個擁有如此少特性的語言。 即使最后他提供了如此多。

            謝謝。
    來自: coolshell.cn

    相似問題

    相關經驗

    相關資訊

    相關文檔

  • sesese色