golang五周歲

題圖來自Gopher illustration by Renee French。見:blog.golang.org/5years

時間過得真是太快,一晃眼golang都五周歲了。五年,對於人生來講也許有些漫長,但對於一門語言的發展來說,僅僅是滄海一粟。相對於它試圖挑戰的「系統級」語言:c,c++,甚至java,golang還年輕得很,就像馬拉松比賽,別人都已經跑完了半馬,golang才剛剛越過起點線。拿最為人詬病的GC說事 —— 和java的GC比起來,golang的粗糙得像個玩具。按照golang的roadmap,1.5版本release後,也就是明年7月份,按官方的原話來說,golang的GC才會有一個 "making Go acceptable for implementing a broad spectrum of systems requiring low response times" [1] 的版本。注意這裡的措辭,是acceptable,不是great,更不是brilliant。

年輕還意味著不成熟。去年我在為別人做的一個兼職項目中,無意中發現了go1.2的編譯器對全局變數處理不當(未使用bss段)導致的可執行文件過大的問題 [2]。連我這種新手都能發現golang的問題,可見其成熟度還有待市場的檢驗。

年輕加上不夠成熟,就意味著敢於嘗鮮的人不多。儘管有google在為其搖旗吶喊,有docker這樣大紅大紫的開源項目不斷背書,golang的使用狀況依舊堪憂:如果你查看11月份的TIOBE指數 [3],golang可憐巴巴地排在了41名,還不及Haskell這樣「小眾」的函數式編程語言。當然,TIOBE未必反應真實的數據,像我這樣整個11月沒有搜索過任何有關golang內容的人 [4],恐怕是被排除在golang用戶之外的。

說了golang的不好,再說說它的好。

語言上的特性,如concurrency support,messaging with channel,type inference,implicit interface,build into one giant executable等等就不必重複,仁者見仁,淫者見淫。這些特性在不同的程序員看來,有見然不同的見解。c程序員見了這些必然兩眼放光,但會對其性能,尤其是處理數據的thoughtput或latency存疑;erlang程序員估計會喜歡大部分特性,但對messaging和concurrency不屑一顧;而python程序員興奮之餘,卻又會產生「要是python能夠產生一個脫離運行時的可執行文件,且沒有GIL作為制肘,那golang還有市場空間么」這樣一廂情願的白日夢。

所以語言特性,並不能成為我為golang叫好的原因。在我看來,golang為軟體開發注入了新的思想和活力,讓很多曾經的「想當然」有了新的思路,這才是最重要的。

試舉一二。

首先,concurrency在別的語言那裡被當做parallelism的一種手段,在golang這裡卻變成了解耦系統,降低複雜度的神器。如果你沒看過Rob Pike的concurrency is not parallelism [5],強烈建議看一下,即使你並不是golang的擁躉。也許是誕生於unix發明人之手(Ken Thompson),golang裡面處處可見unix精髓。chan/go 這對歡喜冤家,就像跳動的精靈一樣,將函數之間亘古不變的調用關係變成了一對對,不,一群群pipe。最容易讓人拿來說事的是 io.Copy():

func Chat(a, b io.ReadWriteCloser) {n err_chan := make(chan error, 1)nn go cp(a, b, err_chan)n go cp(b, a, err_chan)nn if err := <- err_chan; err != nil {n log.Println(err)n }nn a.Close()n b.Close()n}nnfunc cp(w io.Writer, r io.Reader, err_chan chan<- error) {n _, err := io.Copy(w, r)n err_chan <- errn}n

你無法想像比這再優美的代碼:它在a/b兩個流之間進行全雙工非同步通訊,當發生錯誤時告知 Chat() 所在的協程,記錄錯誤並優雅退出。對於golang來說,把 chan/go 當成解耦的工具,運用得當的話,代碼就有了魔力。

其次,golang的某些哲學值得其它語言的使用者思考。golang的語言設計中透著妥協,它期望把concurrency model帶至主流。所以它選擇了C-like的語法,走imperative language的路,而不是把自己打造成functional language,像Haskell那樣,也因此,並發模型上,golang選擇了CSP model [6],因為CSP對imperative language的支持很友好,不必使用一些奇怪的語法和新穎的概念去表達。相比之下,大多functional language都選擇了Actor model [7],如erlang,scala。所以在golang里你見到的是channel,erlang里你見到的是process(actor);golang里的goroutine是匿名的,erlang里的process是有pid的;golang里消息的發送是同步的,阻塞的 [8],erlang里消息是非同步的,非阻塞的,等等等等。

做為一個concurrent language,golang最受詬病的是指針竟然也能夠在channel中傳遞。要知道,concurrency的精髓在於don』t share state。erlang之父Joe Armstrong在其 "Programming Erlang" 中寫到:

We don』t have shared memory. I have my memory. You have yours. We have two brains, one each. They are not joined together. To change your memory, I send you a message: I talk, or I wave my arms.

(題外話:這是本好書,裡面有不少思想,即使不用erlang,也可以讀讀)

所以對於golang的concurrency支持,erlang的使用者,或者有functional programming情節的人,絕對反感。但golang的設計者有其道理:為了讓c/java/python等imperative language的使用者能更好地接納golang;也有其哲學:

Don』t communicate by sharing memory, share memory by communicating.

再次,golang的錯誤處理另闢蹊蹺,在幾乎所有主流語言都支持 try/catch 結構的世界裡,「反人類」地使用了類似於c的通過返回值返回錯誤的「原始」方式。是的,錯誤處理還可以有不一樣的玩法。這種方式簡單清晰,通過有效應用 chan(如上例),錯誤還可以像其它支持 try/catch 的語言那樣bubble up。

我不是語言設計的專家,姑且很不專業地估計一下為什麼golang這麼設計。對於一門以concurrency見長地語言,concurrency必定無處不在的。如果採用異常模式,那問題來了:當一個goroutine有未處理的錯誤,誰來處理這個錯誤?自然是主線程,這是其它編程語言的做法。然而,主線程是否是最佳的選擇?不一定。如果使用 try/catch 結構,沒得選擇,但golang通過使用返回值來返回錯誤,讓開發者可以選擇把錯誤交給哪個goroutine處理。這可以催生一些有意思的模式,比如說有個goroutine專門負責過濾錯誤,只有極其嚴重的才bubble up到主線程,讓其清理資源並退出程序。

當然,這種方式與concurrency放在一起也還是會有一些缺陷,所以golang需要 defer, panic 和 recover 來幫忙。

最後,golang把code by convention玩到了極致。代碼的行為靠約定來完成,大寫字母開頭的函數自動export,implicit interface等等,讓其它語言中需要額外代碼描述的事情在這裡不必贅述,如果不是golang,恐怕很多人都無法想像繼承可以這麼玩。我倒不是說這麼做就是最佳實踐,但它是繼 io [9] 和 javascript 通過原型繼承讓我大開眼界後,又一個讓我感慨一下,思考一下的實踐。

嗯,就寫這些吧。

衷心希望golang在下一個五年能夠大放光彩,也希望那些和程序君一樣的還在和C語言一起奮鬥的一部分可憐人 [10] 可以早日轉戰在golang的競技場。神馬,程序君為什麼不能用golang寫代碼呢?唉,尼瑪golang不厚道,支持freebsd的最低版本是8,誰能幫我在6.x/7.x上編過,務必告知,給跪了。

disclainer: 程序君golang經驗不足一年,除去hello world系列代碼,代碼數量不足5千行,才疏學淺,本文如有錯漏,敬請指教。

如果您覺得這篇文章不錯,請點贊。多謝!

歡迎訂閱公眾號『程序人生』(搜索微信號 programmer_life)。每篇文章都力求原汁原味,早8點與您相會。

1. 見 Go 1.4+ Garbage Collection Plan and Roadmap (golang.org)

2. 見 code.google.com/p/go/is

3. 見 TIOBE Software: The Coding Standards Company

4. 我都是直接godoc,或者上golang.org

5. 見 youtube.com/watch?

6. 見 Communicating sequential processes

7. 見 Actor model

8. golang把這特性玩出了好多花活,見上文所述解耦的部分

9. 見我的博客 有意思的Io語言(一)

10. 不需要soft realtime的場合,golang可以替代C
推薦閱讀:

如何評價 Swift 語言?
為什麼特殊阿拉伯字元串能造成iOS系統和OS X下應用崩潰?解決方案是什麼?15年重現bug。
iOS 8 設備隨機 MAC 地址躲避 Wi-Fi 熱點的記錄追蹤,技術上是怎麼實現,有何影響?
為什麼 Windows 上的第三方軟體對高清屏支持這麼差?
到底有多少人在等待iPhone X?

TAG:迷思 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Go14GarbageCollectionPlanandRoadmapgolangorg | TIOBESoftwareTheCodingStandardsCompany | Communicatingsequentialprocesses | Actormodel | 有意思的Io语言一 |