golang五周岁

2014年11月11日 330点热度 0人点赞 0条评论

题图来自Gopher illustration by Renee French。

时间过得真是太快,一晃眼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) {
    err_chan := make(chan error, 1)

    go cp(a, b, err_chan)
    go cp(b, a, err_chan)

    if err := <- err_chan; err != nil {
        log.Println(err)
    }

    a.Close()
    b.Close()
}

func cp(w io.Writer, r io.Reader, err_chan chan<- error) {
    _, err := io.Copy(w, r)
    err_chan <- err
}

你无法想象比这再优美的代码:它在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, panicrecover 来帮忙。

最后,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. 见 http://llvm.cc/t/go-1-4-garbage-collection-plan-and-roadmap-golang-org/33

2. 见 https://code.google.com/p/go/issues/detail?id=6993

3. 见 http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

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

5. 见 https://www.youtube.com/watch?v=cN_DpYBzKso

6. 见 http://en.wikipedia.org/wiki/Communicating_sequential_processes

7. 见 http://en.wikipedia.org/wiki/Actor_model

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

9. 见我的博客 http://tchen.me/posts/2013-01-06-io-lang.html

10. 不需要soft realtime的场合,golang可以替代C

3120golang五周岁

root

这个人很懒,什么都没留下

文章评论