编程与软件工程yager.io
为什么 Go 并不好
Why Go Is Not Good
2014 — 2021,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
这是一篇 2014 年的博客文章,作者系统性地批评 Go 语言:缺乏泛型导致大量重复代码、错误处理繁琐、nil 与类型系统的缺陷、不支持运算符重载等,认为 Go 算不上「好」语言。文章引发多年反复讨论,也常被 Go 拥护者反驳为「在用错的方式使用语言」。
为什么经久不衰:它精准概括了对 Go 最经典的批评清单,而 Go 此后十余年在泛型、错误处理上的演进,恰好让这篇文章成为衡量语言争论变化的标尺。
HN 评论精选
Go 的简单性刻意牺牲表达力,换取系统编程中可控的复杂度。
Rust、Scala、Haskell 甚至 C++ 这类语言很诱人,它们仿佛在耳边说「你很聪明,这块空白画布随你设计史上最完美的抽象」。但在系统编程里抽象是有代价的,抽象一旦出问题,你不仅要面对坏掉的系统,还要面对坏掉的抽象本身。Go 的价值正在于此:它不让你把事情做得漂亮,却让你几乎无法把东西过度复杂化。写 Go 时你能想象出对应的 C 代码长什么样;要处理错误?一个 if 语句。要数据结构?一个 struct。要泛型?再来个 if 放进 for 循环里。Go 显然不适合大多数场景,应用开发也许负担得起抽象的成本,但对于只需把一件事做到极致的工具,要么它要么 C,而我不打算回去自己管理内存。
mustpax查看英文原文
There's something very seductive about languages like Rust or Scala or Haskell or even C++. These languages whisper in our ears "you are brilliant and here's a blank canvas where you can design the most perfect abstraction the world has ever seen." But, for systems programming, abstractions suck. They always, always have a cost. When abstractions break, you not only have to deal with a broken system but the broken abstraction itself too. (Anyone who has ever seen a gcc compiler error for C++ knows how this feels.) Therein lies Go's value proposition. It does not make it possible to make things pretty (ugh, nil). It just makes it impossible (ok, really hard) to overcomplicate things. When you write Go code, you can picture what the C equivalent would look like. You want to deal with errors? Here's an if statement. Data structures? Here's a struct. Generics? Here's another if statement, put it inside your for loop. Obviously, Go is not the right choice of language for most things. When you're doing application development, you may be able to afford the cost of abstractions. But for tools that only need to do one thing and do it extremely well, it's either that or C. And I'm not going back to managing my own memory anytime soon.
许多被指为缺陷的问题其实有惯用替代方案,属于使用方式问题。
冒着被踩的风险说:文章里很多抱怨其实是「跟语言较劲」的问题。「对任意列表求和」在 Go 里并不需要用文章设想的那种参数化多态,你可以定义 type Adder interface{Add(Adder)Adder},然后 func Sum(a ...Adder) Adder 就很轻松,放什么进去都行,出来再断言类型。迭代也有生成器模式:返回 channel,然后 range 消费到干为止,比如 func (m myType) Walk() chan myType 可以写成 range v := mt.Walk(){...};也有非 channel 的模式,分词器通常有 Next() 逐步推进。据我所知 nil 指针并不不安全(见官方 FAQ 关于无指针运算的说明),作者似乎以为对 nil 指针调用函数会崩溃,其实不是,惰性构造里先检查接收者是否为 nil 是常见做法。Go 绝非完美,但它支撑了一种简单而有力的编程风格,我个人很喜欢。
tshadwell查看英文原文
For fear of disagree downvotes: I would say that many of the qualms brought up in this article are problems that are encountered fighting the language. The problem of 'summing any kind of list' is not a problem that is solved in Go via the proposed kind of parametric polymorphism. Instead, one might define a type, `type Adder Interface{Add(Adder)Adder}`, and then a function to add anything you want is fairly trivial, `func Sum(a ...Adder) Adder`, put anything you want in it, then assert the type of what comes out. When it comes to iteration, there is the generator pattern, in which a channel is returned, and then the thread 'drinks' the channel until it is dry, for example `func (m myType) Walk() chan->myType` can be iterated over via `range v := mt.Walk(){ [...] }`. Non-channel based patterns also exist, tokenisers usually have a Next() which can be used to step into the next token, etc. The Nil pointer is not unsafe as far as I know, from the FAQ: http://golang.org/doc/faq#no_pointer_arithmetic The writer seems to believe that functions on nil pointers crash the program, this is not the case. It's a common pattern in lazy construction to check if the receiving pointer is nil before continuing. Go is not flawless by any means, but it warrants a specific style of simplistic but powerful programming that I personally enjoy.
Go 在常规代码上高效愉悦,同时允许用 unsafe 在关键处突破类型系统。
我花了一个周末学 Go 并写了一个单写多读的线程安全哈希表。我故意选它,因为这违背 Go「通过通信共享内存而非直接共享数据结构」的哲学。写起来很痛苦:必须用 unsafe 和 atomic 手工保证 key 与 value 的写入顺序。但代码中非 volatile、非 unsafe 的部分写起来非常愉快:测试愉快、编译愉快、基准测试愉快。我很惊讶它能让我完全绕过类型系统,为性能做那些脏活。我想要一门语言,在我必须时可以干脏活,但让其余 95% 的代码以及编译、测试、维护都变简单——Go 在这点上很出色;Rust、C++、Haskell、Scala 永远做不好这一点,因为它们太复杂了(尽管它们让脏活稍微不那么痛苦)。周末折腾的结果是,在 i7-4770K 上我的实现比 Go 内置 map 快约 4 倍。
eloff查看英文原文
I just spent the weekend learning Go and writing a single writer multi-reader hashtable for threadsafe access. I picked it deliberately because it's against the philosophy of the language, which is to share by communicating instead of sharing data structures directly. It was painful to write: // Do NOT reorder this, otherwise parallel lookup could find key but see empty value atomic.StoreInt32((*int32)(unsafe.Pointer(uintptr(uint64(slot)+4))), val) atomic.StoreInt32((*int32)(unsafe.Pointer(slot)), key) However, the non volatile, non unsafe parts of the code were an absolute joy. Testing was a joy, compiling was a joy, and benchmarking was a joy. I was impressed that it allowed me to bypass the type system completely and do nasty, nasty things in the pursuit of performance. I want a language that lets me do nasty things where I must, but that makes the other 95% of the program, and the job of compiling, testing, and maintaining that program easy. Go excels here. Rust, C++, Haskell, Scala will never be good at that because they're too damn complicated (although each of them make the nasty parts a little less painful!) The end result of my weekend's hacking? On an i7-4770K @ 3.5ghz BenchmarkGoMapInsert-4 20000000 110 ns/op BenchmarkHashIndexInsert-4 100000000 25.6 ns/op BenchmarkGoMapLookup-4 50000000 78.5 ns/op BenchmarkHashIndexLookup-4 100000000 17.7 ns/op About 4x faster than Go's builtin map, for int32 key/value on both insert and lookup. And it allows any number of reader threads concurrent access with a writer without any synchronization or blocking, unlike Go maps. It doesn't allow zero keys, unlike go maps, and it doesn't allow deletes. Hardly apples to apples, but the performance of pure Go code is impressive nonetheless. 200 LOC, not counting tests.
单点功能对比忽视整体权衡,作者低估了改动带来的连锁代价。
这类无端的「拆台」文章是 HN 的毒品:一页又一页地讲「这个流行东西如何不像我喜欢的另一个东西」,却完全不考虑事情为什么是这样。Go 也缺很多我想要的功能,但我知道它的作者很聪明,不会立刻假设他们不懂自己在干什么。思想实验:写一份提案,为 Go 加入代数数据类型(或者哪怕只把错误处理特化成 option 类型)。我试过,发现会牵出一堆其他问题,使方案并不显然可行(比如你还需要 match 运算符,那意味着所有语句都要能作为表达式,还得改变遍布全语言的零值语义)。而我很喜欢 Haskell 的代数数据类型。说到底,如果你真想要 Haskell,就该直接用 Haskell 或 Rust;然后你会发现那些语言也有它们的问题,并明白工程是权衡,而不是这篇文章这样的功能清单打勾。
evmar查看英文原文
This sort of gratuitous takedown is unfortunately crack for HN -- pages and pages of "here's how this popular thing is not like this other thing I like", without any thought given to why things are they way they are. Go is missing a lot of my pet features too but I know its authors are smart so I don't just immediately jump to assuming they don't know what they're doing. Thought experiment: write a proposal that works through adding algebraic data types (or even just special-case the error handling as option types, if that is easier) to Go. I've tried it; I found that doing so brings up a bunch of other problems that don't make it an obvious solution. (E.g. you'll want a "match" operator. And then that means you need all statements work as expressions. And you'll have to change how zero values work, which are pervasive throughout the language.) And I really like algebraic data types in Haskell. At some point if you really want Haskell you should just use Haskell. Or Rust. And then you will find out that those languages have problems too, and you will understand that engineering is a question of tradeoffs, not of feature checklists like this blog post.
历次讨论
同类还值得读
开源应用架构
The Architecture of Open Source Applications
《开源软件架构》(AOSA)是一本在线免费书,邀请知名开源项目的作者亲自剖析其内部设计,涵盖 SQLAlchemy、LLVM、Audacity、Apache、Bash 等系统。
编程与软件工程上首页 9 次 · 2011–2024aosabook.org编辑精选编程语言简史:简短、不完整且多半错误
A Brief, Incomplete, and Mostly Wrong History of Programming Languages
一篇以戏谑、故意不准确的方式回顾编程语言史的博客文章,用夸张轶事和讽刺定义串起 Lisp、C、C++、Java、JavaScript、PHP 等语言。
编程与软件工程上首页 8 次 · 2009–2021james-iry.blogspot.comSQLite 是如何测试的
How SQLite is tested
SQLite 官方文档页,系统介绍 SQLite 的测试体系:多种测试方法(TCL 测试、模糊测试、回归测试、静态分析等)和海量测试用例,展示了这个小型开源项目如何靠极致测试获得…
编程与软件工程上首页 8 次 · 2009–2025sqlite.org编辑精选高级编程语言(2009)
Advanced programming languages (2009)
Matt Might 的经典文章,按用途推荐“高级编程语言”:如果追求就业就选主流语言,如果追求表达力、学术研究或创业,则推荐 Haskell、OCaml、Scala 等函数式语…
编程与软件工程上首页 8 次 · 2009–2022matt.might.net我的 Forth 与栈机器经历
Yosefk - My history with Forth & stack machines
作者以亲身经历回顾了使用 Forth 语言和栈式机器的历程:既赞叹其简洁优雅、代码量小、可自举的优点,也坦率指出栈操作在实际编程中的痛苦,以及 Forth 社区文化中的问题。
编程与软件工程上首页 8 次 · 2010–2024yosefk.com编辑精选