這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文在這裡:http://www.syntax-k.de/projekte/go-review。作者是 Jörg Walter,是個德國人。
根據 Rob Pike 在推上的說法,文章有一些錯誤,但看起來值得閱讀。
“Why all C-like languages except one suck”: http://www.syntax-k.de/projekte/go-review Some errors in there but a positive response.
———————–翻譯分割線———————–
Go程式設計語言,或者:為什麼除了它,其他類C語言都是垃圾
2011-06-07
Jörg Walter 撰寫
簡介
這是關於 Robert Griesemer,Rob Pike 和 Ken Thompson 在 Google 從 2007 年開發的 Go 語言的綜述。現在,Ian Taylor,Russ Cox 和 Andrew Gerrand 已經加入了核心團隊。這是一個類 C 語言,有一些動態指令碼語言的特性,並且提供了一些關於並行和物件導向的新奇的(至少在通用語言領域)特性。它的目的是成為系統開發語言,這也是為什麼這篇綜述選擇同其他類 C 語言,而不是指令碼語言進行比較。
在編寫這個綜述的時候我發現,與其對 Go 進行評估,不如說它有更多值得詳細解說的功能。Go 是完全不同的,不能用標準的 OO 背景知識去判斷它。因此,這個綜述可能更像是一個關於 Go 的介紹。我是一個 Go 新手。編寫這個也協助我去瞭解 Go 是什麼,並且如何做,但務必記得我的第一個 Go 應用只開發了一半。我用過許多語言進行編碼,因此將會用 Go 同它們做一些比較。
Go 很年輕。它在今年才被標識為穩定版本。通過這個綜述,我同樣期望能夠對 Go 未來的方向的討論做一些貢獻,使其成為一個了不起的語言。 這包括了指出仍然存在的一些缺陷。
關於 C 系語言的咒罵
我總是對新的程式設計語言感興趣。通常,我使用方便的動態語言,例如 JavaScript、Perl 或者更新一些的 Python。大多數情況下,我更喜歡可讀的、可維護的、高開發效率超過單一的效能測試。過早的最佳化通常都不值得。安全也是一個方面,而指令碼語言將你從緩衝區溢位、字串格式化缺陷和其他底層攻擊的世界中解救出來(假設無法利用運行時環境)。
但是這些語言也有負面問題。它們不夠靈活,而我的工作從 ARM 系統上的 8-bit MCU 和智能手機到標準的案頭應用。我嘗試在其上使用C(++),但那使我感到很痛苦。沒有便利的字串運算子、依賴外部庫的笨重的Regex、手工內容管理,當然還有持續了 40 年的安全夢魘。除此之外它能很好的工作,提供了幾乎能實現的最快的記憶體效率。
在底層語言中,缺失了一些基礎的東西。用於編寫底層工具和作業系統的東西,都相當古老,並且被現代系統內容所遺忘。這就是我這裡所期望展示的。僅涵蓋了 C 血統語言,就像人們習慣的那樣, 但是你可以很容易的增加 Pascal、Modula、Oberon、Smalltalk、Lisp 以及其他任何在電腦系統中做過核心的語言。
C
我喜歡 C。確實是這樣。它的簡單使得其相當優美。除非你去做一些愚蠢的事情,例如用 C 作為主要 API 來編寫複雜的 GUI 開發包。
一個的確非常好的事情是,你可以像用彙編那樣進行控制,在特定情境中這確實是非常重要的。除了進行最佳化,絕對是。然後救是來自 C 的令人費解的文法的回擊,例如不能對存放敏感性資料的記憶體回收,或者在記錄聲明之間進行同步的障礙。這不可避免。如果有指標和運算,就需要語言中隱含的語義來保證最佳化不會變得更糟。
但是真正糟糕的是字串、記憶體管理、數組等等……任何可能被利用的東西。並且難以使用。所以,C 可能是像其表現的那樣輕量級的,它並不適合那些超過 10k 行代碼的項目(譯註:偏激了點,不是嗎?)。
C++
C++ 改進了 C 的一些方面,但是帶來了其他一些更糟糕的東西,尤其是無用的冗長的文法。你總是能找到那些束縛。不過對於標準應用程式開發它總是被優先選擇的,一些不錯的輕量的全功能 GUI 開發包很好的使用了這些優點。
但是當你嘗試一些現代的動態語言功能(匿名函式、map/reduce、類型推斷……)時,通常都是這個路子“嘿,很酷!這個其實可以用 C++ 實現!你只需要這樣做
dynamic_cast<MyData*>(funky_iterator<MyData &const*>(foo::iterator_type<MyData>(obj))
好了,就這樣!”
別誤導我,我鐘愛模板,但是使用 STL 的 C++ 看起來就像一個標準的情況“如果你只有一把鎚子”綜合症。GCC 不得不實現特別的診斷和簡化,最終發現其實長達 5 行的錯誤訊息只是在使用 std::string 方法時,簡單的常量化錯誤。同樣糟糕的是,它們像地獄般的緩慢。那麼等待 Boost 編譯如何?好的主意,糟的現實。
Objective C
現在我覺得自己有點像異教徒。我不應該貶低任何來自 NeXT 的東西。但是,我忍不住——束縛感同樣存在於 Objective C。它的冗長並不像 C++ 那樣,但是括弧文法就像進入來 C 的平行世界,這樣 C 的全部文法都可用。
如果你對在 C++ 中編寫模板轉換不是那麼印象深刻(這或許是個優點^^),並且仍然在使用手工方式管理記憶體。在 C++ 中至少可以用引用計數的方式釋放記憶體(如果你曾經在數千錯誤的模板中找到文法正確的那個)。
Objective C 針對這個問題,跨越性的官方提供了可選的 GC。等等——這對於 C 系語言來說總是可選的。Boehm-GC 存在了多少年了?但是標準是採用記憶體池,在許多情況下很好的相容性使得其能夠工作,但是相容性也就好成那樣。
Java
你不會真以為在咒罵 C 系語言的時候我會忘記 Java,是嗎?當前來說,Java 幾乎總是解決方案。幾乎得有點不真實。
有二進位誇平台能力,有著各種實現的、沒有重大陷阱的語言細節的規格說明書,標準的 OO,記憶體回收,一些真正的動態特性,最終甚至是泛型——以及禁止消耗記憶體和拖慢啟動時間。
其實沒有明確的需求來解釋為什麼是這樣。增強 JIT 編譯器(理論上)可以比其他任何預先進行最佳化的靜態編譯器做得更好。GCJ 編譯器可處理機器碼,如果你希望它這麼做的話。VM 也有完全符合的硬體實現。但是 JDK 使得基礎臃腫,同時許多 Java 項目也有著拜占庭風格的複雜(譯註:拜占庭風格總是絢麗且奢華的,但是似乎總有點華而不實,不是嗎?)。
現在,可以很舒適的編寫現代的 Java 了。Web 服務提供了一些真正出色的抽象。直到你掀開帽子,然後發現一些小題大做的機制。每一層都用上年最流行的抽象層構建。你不能對向下相容妥協,對嗎?
看看你通常的 Web 應用程式的庫目錄。我不會對看到上百個 JAR 檔案感到驚訝,對於用 PHP 實現的簡單的資料庫搜尋或者購物網站,可以在 10k 行代碼以內。或者你很有冒險精神,嘗試自行編譯。世界上最有樂趣的事情!設定一個 Linux 系統,沒有任何 step-by-step 的介紹,這很容易。相信我,我都做過。在你開始之前,請務必確保你知道如何正向和反向拼字“dependency hell”(依賴的地獄)。
而所有這些都包含在繁瑣的文法和古老的學校裡的物件模型中。這基本上沒有什麼錯誤,但是有更好的辦法。藝術品味是另一回事。看看 Perl6,嘗試將所有現代語言設計放到一起,使其成為可用(真正有價值的“可用”)的語言。而其第一個功能生產可用的版本已經有十年以上了!Java 現在也快如此了,除了泛型以外。
C#
我差點忘了這個。其實我確實忘了這個,直到有反饋提醒了我。坦白說,我不瞭解 C#。作為語言,它看起來不錯,C 和 C++ 的很好的發展。讓我對其保持距離的是它非自由的出身。是的,有 Mono,但是我從不喜歡讓我的工作建立在一個微軟隨時可以轉為專利訴訟的慈善性質許可的語言上。我們都知道關於這個公司(好吧,實際上是任何大公司)實施它奴役的詭計。
我看不到編寫不能誇平台的代碼的亮點,因此嘗試 Mono 對於我來說是種危險,我總是遠離它。同樣,當 Java VM 的強勢和弱勢被廣為流傳的時候,CLR 想必也已經獲得了足夠多的聲譽。我可能會在某個時間寫 C# 代碼,但這天不會很快到來。
沒有開放的生態系統的促進,以及特殊目的的實現,它不適合作為系統程式設計語言。僅僅適合企業開發。
哦,還有企業期望從語言的開發中賺取金錢,這不是一個健康的發展途徑。商業興趣將會強加一些對於語言不好的東西。改進的壓力長期存在,否則就賣不出去。作為對比,來看看 Tex,有著 30 年以上曆史的,並且作為軟體可能達到的無 bug 的狀態。無法真正對比兩者,不過可以展示哪裡是終點,而 C# 站在了錯誤的地方。
JavaScript
JavaScript 似乎不應該出現在這兒,因為它是一個完全動態語言。它是最廣泛使用的語言之一,然而,它也是 C 系的。並且,更重要的是,JS 引擎這段時間支援了相當進階的 JIT 編譯器最佳化,因此效能同這裡提到的其他語言相差無幾。
那麼,JS 有什麼本質錯誤呢?不多。只是它不適宜用在小型系統上。它是很棒的應用嵌入式語言,也適合編寫網路服務,但是設計使得它沒有辦法同外部世界互動。主應用在實現容器中定義了所有的互動 API,因此決定了它無法作為一個系統的主語言。
更進一步,JIT 和運行時的需求局限了它的嵌入用途。如果有一個 Palm Pre,並使用 JS 作為嵌入語言,這是相當方便的。當 500MHz/256MB 的系統是底線時,是可用的。可能最低的使用 JS(或者 ECMAScript) 作為系統主要語言的裝置,是 Chumby,在 450MHz/64MB 上播放 Adobe Flash Lite 影片。那裡並沒有真正的通用語言。
心愿
親愛的聖誕老人,所有的底層語言都很爛。聖誕節我想要一個有以下特點的程式設計語言(用已有語言作為例子),排名不分先後:
一般設計原則
1. 表達能力
我期望有一個語言可以有足夠高的層次用來表達我的想法和演算法,而不是在乏味的任務管理或 80% 都是模板代碼上浪費時間和螢幕空間。更重要的體現表達能力的元素被單獨列出。編寫詞法分析器是一個好的測試。如果最終的結果代碼與分析的構造有許多相似之處,這就是一個正確的方向。(例如:Perl 6 的文法)
2. 簡單
我期望有一個語言有著優雅的簡單。只需要幾個概念就可以表達所有的可能。正交性是這個情況的關鍵。簡單也使得語言容易學習。在相同的目的中重用文法結構,並且讓使用者代碼與這些結構對接,這樣就可以在其上進行擴充。同樣的,不要強迫使用者使用複雜的結構。提供類、單元測試架構或者文檔注釋沒有錯,但是如果使用者不想要的話,不要讓它們進入視線。 理想情況,讓它們“Do what I mean”。
3. 平等權利
如果內建的聯合數組演算法對於特定的資料集是低效的,我期望可以建立一個能像內建那個一樣使用的實現來代替。語言不應該有特權。因此,操作符應當可以被重載。噢,在 Python 裡做得那些魔法般的事……同樣,內建資料類型應當可以作為對象文法在語言中使用。所有這些無須動態,多數都可以用靜態文法糖來實現。(例如:Perl 的 prototyped subs,Python 的操作符重載)
4. 元編程
這裡有兩個方面。一個是模板以及/或者泛型,這太有用了,以至於不能沒有。雖然 C 有一些邪惡的 hack 來類比這個:預先處理程式。每個人都需要這個。更加進階的方面是編譯時間執行代碼產生代碼,像 lisp 的宏和 Perl 的代碼 filter。額外說明一下,這允許建立新的語言結構或者特定領域的語言。
5. 高效的和可預測的
如果語言瞄準的是系統編程,它必須相當高效並且完全可以被預測。必須能夠直觀的預測確定的操作帶來了哪些時間消耗和記憶體配置。核心的語言功能必須很好的最佳化。庫提供了更高層次的結構使得編碼更有效率,而執行卻低效一些。對象系統無須昂貴的運行時支援(不論是時間還是空間),靜態最佳化應當是可能的。
理想情況,用幾千行代碼和幾百位元組記憶體編寫一個有用的控製程序是可能的。當然,效率和功能通常作用相反,需要取捨,所以語言應當提供這樣的機會讓我來決定。
6. 可用性
最終,語言必須可用。所有想法放到一邊,最重要的還是它解決實際問題。用一點實用主義不會有傷害。語言應當與其他語言接近,這樣就可以用之前思考的模式去思考它。如果嚴重的偏離常規,那它就必須是值得的。忠於原則,而避免驚異!
7. 模組化的庫和代碼倉庫
我期望在成長過程中使用過的指令碼語言內建的或部分標準庫的那種精巧。一個包的公用代碼倉庫和適當的可移植的包管理則更好。通常,包包含網路通訊協定、常用格式的解析、GUI、加密、通用數學演算法、資料處理等等。(例如:Perl 5 CPAN)
特別的功能
8. 資料結構
我要我的雜湊!一個沒有同語言良好整合的聯合數組資料類型的語言是一個笑話。顯而易見,OO(或者其他便利的封裝)也是必須的。布爾類型很好,但是更重要的是各種類型在布爾上下文中的明確的解釋。額外的,在標準庫中有多種進階資料結構,例如不同的樹、列表、集合等等。如果已經有了跳躍鏈表,那就是上軌道了。
讓字串也是 OO 的,尤其是相似的運算子(如 len())作用於字串應當像作用在數組或類比數組的對象上一樣。附加的,還要看是否所有未經處理資料類型支援與其他所有對象相同的 OO 文法。無須像 Ruby 那樣。只要有文法糖即可。(例如:Python 和它的標準庫)
9. 控制結構
聽上去顯而易見,不過允許一次從多個嵌套的迴圈中跳出的控制是恰當的。消除 goto,真的。上一次我用到它是……好吧,就是上周。我在奧爾登堡電腦博物館裡的 Commodore C64 上做懷舊程式示範。這不算。所以丟掉 goto 吧,但是給我 foreach。除此之外別無所求。JavaScript 的 with 語句我從來沒有用過,不過這也是個好主意,我想這是某種形式的“少即是多”。(例如:所有的指令碼語言在這方面都很出色)
事實上,有一些東西在其他地方還從未見過。之前,我遇到過許多迴圈,每個迴圈的入口和條件測試/迴圈退出都不相鄰。那麼如果有一種方法來表達一個迴圈從中間某個位置開始,看起來會很酷。否則,就需要複製迴圈的一些部分。有點類似 Duff 的裝置,不是為了最佳化,僅僅是讓代碼不要那麼冗長。
10. 運算式文法
許多人刻意避開,但是 ?: 三元運算子是個好主意,如果使用正確的話。Python 實現了 foo if bar else baz,有點羅嗦,不過也還算 OK。然而,JS 和 Perl 的布林運算子 AND 和 OR 不是用來計算 true 和 false 的,但是對於特定的值可以被認為是 true。假設賦值 value = cmdline_option || “default”。這需要在所有資料類型上有恰當的布爾含義。
11. 運算式的函數性質
如果我想要寫 Lisp,我會這麼做的。我並不需要一個完整的函數程式設計語言。但是沒有什麼比 map() 更好的了。閉包和匿名函數(lambda運算式)也是殺手級的功能。可能“超級複雜”領域是 hyper 運算子(在 Perl6 中是這麼叫它們)如 any() 和 all() (在 Python 中是這個名字),但是它們都很棒,並且暗含著並行的含義。歡迎來到新世紀,至少是九十年代吧。
12. 對象
已經有若干物件導向的模型,但是至少需要封裝和多態。有一些組合類別的方法也同樣存在,如繼承和混合。介面應該有,或者用多重繼承來代替(譯註:多重繼承?夢魘,絕對的夢魘!)重載是很重要的,或者至少給我預設參數。具名引數也是很酷的辦法。
13. 並行
對於這種情況我是個失敗者。generator 和協程在某些情況下適用於此。要點是不要內建的多線程,但是讓多個代碼方便的在一起工作。它們不需要同時執行,但是應當可以同時工作於資料集的多個部分。將應用構造成事件驅動應當比較容易。這一機制對於真正的多核來說,非常棒。(例如:Perl5 的 POE,python 的 generator)
14. 字串和 Unicode
這是該死的 2011,除了 Unicode 什麼都不需要。所以務必包含安全的字串類型,並且所有都是 Unicode 的,不要有例外。我對額外的雙編碼的隱式轉換感到噁心,也不想再見到,或著手工轉換而不是語言支援。順便說一下,我更喜歡 UTF-8。誰會在意常量字串的索引呢?在這種特例下用字元數組吧。一般情況使用Regex。在原始字串的 API 上要有Regex支援。
15. 底層介面
有觀點認為,可能會需要手工操作 bit。特別當目標是微控制器或嵌入式 ARM 核心,應當有方法嚇到裸裝置去。理想情況是用語言直接編寫 OS 核心,而無須任何彙編(除了平台特定的啟動代碼,它們沒辦法用其他辦法完成)。
———————–翻譯分割線———————–
這文章實在是太長了,拆分吧。今天的任務到此結束。
To be continue…