加载中

I use Go at my current day job. I've gotten pretty familiar with it. I do not like it, and its popularity is baffling to me.

Developer Ergonomics

I've never met a language lead so openly hostile to the idea of developer ergonomics. To pick one example, Rob Pike has been repeatedly and openly hostile to any discussion of syntax highlighting on the Go playground. In response to reasonably-phrased user questions, his public answers have been disdainful and disrespectful:

Gofmt was written to reduce the number of pointless discussions about code formatting. It succeeded admirably. I'm sad to say it had no effect whatsoever on the number of pointless discussions about syntax highlighting, or as I prefer to call it, spitzensparken blinkelichtzen.

From a 2012 Go-Nuts thread, and again:

Syntax highlighting is juvenile. When I was a child, I was taught arithmetic using colored rods (http://en.wikipedia.org/wiki/Cuisenaire_rods). I grew up and today I use monochromatic numerals.

我每天的工作都用go语言,我对它很熟悉,但是我并不喜欢go语言,也不懂为什么这语言能火,下面从几个角度阐述。

开发者工程学

    我从没见过一门语言能引发如此公然地敌对开发者工程学。举个例子,Rob Pike(go语言之父)多次公然反复地反对任何关于go语言平台的语法高亮,而且对于很多不错的用户问题,他的公开回答是傲慢而无礼的。

Gofmt编写出来就是为了减少关于代码格式化的无意义的讨论。我很遗憾的说,再多的关于语法高亮的无意义的讨论都是无用功,或者我更愿意称这为“spitzensparken blinkelichtzen”(用于嘲讽输错命令的用户)。

 上述来自于 2012 Go-Nuts thread,除此之外

语法高亮是幼稚的,我三岁学算术的时候都不用彩色棒了(http://en.wikipedia.org/wiki/Cuisenaire_rods)。现在我只用单色的数字符号。

Clearly nobody Rob cares about has ever experienced synaesthesia, dyslexia, or poor eyesight. Rob's resistance to the idea has successfully kept Go's official site and docs highlighting-free as of this writing.

The Go team is not Rob Pike, but they've shared his attitude towards ergonomics in other ways. In a discussion of union/sum types, user ianlancetaylor rejects the request out of hand by specifically identifying an ergonomic benefit and writing it off as too minor to be worth bothering:

This has been discussed several times in the past, starting from before the open source release. The past consensus has been that sum types do not add very much to interface types. Once you sort it all out, what you get in the end if an interface type where the compiler checks that you've filled in all the cases of a type switch. That's a fairly small benefit for a new language change.

问题在于Rob所关心的人之中没人经历过阅读困难、视觉障碍、联觉。Rob对这个想法的不认可使得Go的官方网站和文档编写一直处于强调自由状态。

Go团队不同于Rob Pick,但是他们在其他方面分享了他对人机工程学的看法,在一个关于union/sum类型的讨论中,用户ianlancetaylor直接拒绝了这一要求,他特别指出了一个符合人体工程学的好处,并认为它太微不足道,不值得费心:

 

从开放源码发行版之前开始,这在过去已经讨论过好几次了。过去的共识是sum类型不会向接口类型添加太多内容。一旦你把它们都整理好,最后你会得到什么,如果一个接口类型,编译器检查你已经填入了一个类型开关的所有情况。对于一种新的语言变化来说,这是一个相当小的好处。

 

This attitude is at odds with opinions about union types in other languages. JWZ, criticising Java in 2000, wrote:

Similarly, I think the available idioms for simulating enum and :keywords fairly lame. (There's no way for the compiler to issue that life-saving warning, `enumeration valuex' not handled in switch'', for example.)

The Java team took criticism in this vein to heart, and Java can now emit this warning for switches over enum types. Other languages - including both modern languages such as Rust, Scala, Elixir, and friends, as well as Go's own direct ancestor, C - similarly warn where possible. Clearly, these kinds of warning are useful, but to the Go team, developer comfort is not important enough to merit consideration.

这种态度与其他语言中关于union类型的观点不一致。 JWZ 在2000年批评Java时写道:

同样,我认为用于模拟枚举和关键字的可用习惯用语相当蹩脚。 (例如,编译器没有办法发出救命的警告,“枚举值x'没有在switch''处理。)

Java 团队听取了在这方面的批评,现在 Java 可以为 switch 发出此警告在枚举类型。 其他语言 - 包括Rust,Scala,Elixir和朋友等现代语言,以及Go自己的直接祖先,C  - 同样在可能的情况下发出警告。 显然,这种警告很有用,但对于Go团队来说,开发人员的舒适度并不重要,值得考虑。

Politics

No, not the mailing-lists-and-meetups kind. A deeper and more interesting kind.

Go is, like every language, a political vehicle. It embodies a particular set of beliefs about how software should be written and organized. In Go's case, the language embodies an extremely rigid caste hierarchy of "skilled programmers" and "unskilled programmers," enforced by the language itself.

On the unskilled programmers side, the language forbids features considered "too advanced." Go has no generics, no way to write higher-order functions that generalize across more than a single concrete type, and extremely stringent prescriptive rules about the presence of commas, unused symbols, and other infelicities that might occur in ordinary code. This is the world in which Go programmers live - one which is, if anything, even more constrained than Java 1.4 was.

政治

不,不是邮件列表和会议类型。一种更深入更有趣的类型。

就像每一种语言一样,go是一种政治工具。它体现了一组关于软件应该如何编写和组织的特定信念。在Go的例子中,语言体现了由语言本身强制执行的“熟练的程序员”和“不熟练的程序员”的极其严格的等级体系。

在非熟练的程序员方面,该语言禁止被认为是“太高级”的功能。“go”没有泛型,也没有办法编写更高阶的函数,这些函数泛化于一个以上的具体类型,以及关于逗号、未使用的符号和其他通常可能发生的不道德行为的极其严格的规定性规则。代码。这是GO程序员所生活的世界——一个比Java 1.4更具约束性的世界

On the skilled programmers side, programmers are trusted with those features, and can expose things built with them to other programmers on both sides of the divide. The language implementation contains generic functions which cannot be implemented in Go, and which satisfy typing relationships the language simply cannot express. This is the world in which the Go implementorslive.

I can't speak for Go's genesis within Google, but outside of Google, this underanalysed political stance dividing programmers into "trustworthy" and "not" underlies many arguments about the language.

在熟练的程序员方面,程序员对这些特性是可信的,并且可以将用它们构建的东西暴露给分歧双方的其他程序员。语言实现包含通用函数,这些函数不能在go中实现,并且满足语言无法表达的类型关系。这就是Go实施者所生活的世界。

我不能说google是go的起源,但在google之外,这种将程序员划分为“值得信赖”和“不值得信赖”的未经分析的政治立场是很多关于语言的争论的基础。

Packaging and Distribution of Go Code

go get is a disappointing abdication of responsibility. Packaging boundaries are communications boundaries, and the Go team's response of "vendor everything" amounts to refusing to help developers communicate with one another about their code.

I can respect the position the Go team has taken, which is that it's not their problem, but that puts them at odds with every other major language. Considering the disastrous history of attempts at package management for C libraries and the existence of Autotools as an example of how this can go very wrong over a long-enough time scale, it's very surprising to see a language team in this century washing their hands of the situation.

Go Code的打包和分发

go get是一个令人失望的放弃责任。包装边界是通信边界,Go团队对“供应商一切”的回应等于拒绝帮助开发人员彼此就其代码进行通信。

我可以尊重Go团队所采取的立场,这不是他们的问题,但这使得他们与其他主要语言不一致。考虑到C库包管理尝试的灾难性历史以及Autotools的存在作为一个如何在足够长的时间范围内出错的例子,看到本世纪的语言团队洗手是非常令人惊讶的情况。

GOPATH

The use of a single monolithic path for all sources makes version conflicts between dependencies nearly unavoidable. The vendor workaround partially addresses the problem, at the cost of substantial repository bloat and non-trivial linkage changes which can introduce bugs if a vendored and a non-vendored copy of the same library are linked in the same application.

Again, the Go team's "not our problem" response is disappointing and frustrating.

GOPATH


对所有源使用单个单片路径使得依赖关系之间的版本冲突几乎不可避免。供应商的解决方案部分地解决了这个问题,代价是存在大量的存储库膨胀和非平凡的链接更改,如果在同一个应用程序中链接了相同库的出售和非出售副本,则可能会引入错误。

再一次,Go团队的“不是我们的问题”反应令人失望和令人沮丧。

Error Handling in Go

The standard Go approach to operations which may fail involves returning multiple values (not a tuple; Go has no tuples) where the last value is of type error, which is an interface whose nil value means “no error occurred.”

Because this is a convention, it is not representable in Go's type system. There is no generalized type representing the result of a fallible operation, over which one can write useful combining functions. Furthermore, it's not rigidly adhered to: nothing other than good sense stops a programmer from returning an error in some other position, such as in the middle of a sequence of return values, or at the start - so code generation approaches to handling errors are also fraught with problems.

It is not possible, in Go, to compose fallible operations in any way less verbose than some variation on

    a, err := fallibleOperationA()
    if err != nil {
        return nil, err
    }

    b, err := fallibleOperationB(a)
    if err != nil {
        return nil, err
    }

    return b, nil

In other languages, this can variously be expressed as

    a = fallibleOperationA()
    b = fallibleOperationB(a)
    return b

in languages with exceptions, or as

    return fallibleOperationA()
        .then(a => fallibleOperationB(a))
        .result()

in languages with abstractions that can operate over values with cases.

This has real impact: code which performs long sequences of fallible operations expends a substantial amount of typing effort to write (even with editor support generating the branches), and a substantial amount of cognitive effort to read. Style guides help, but mixing styles makes it worse. Consider:

    a, err := fallibleOperationA()
    if err != nil {
        return nil, err
    }

    if err := fallibleOperationB(a); err != nil {
        return nil, err
    }

    c, err := fallibleOperationC(a)
    if err != nil {
        return nil, err
    }

    fallibleOperationD(a, c)

    return fallibleOperationE()

God help you if you nest them, or want to do something more interesting than passing an error back up the stack.

Go中的错误处理


可能失败的操作的标准Go方法涉及返回多个值(不是元组; Go没有元组),其中最后一个值是类型错误,这是一个接口,其nil值表示“没有发生错误”。

因为这是一个约定,所以它在Go的类型系统中是不可表示的。没有通用类型表示错误操作的结果,可以在其上编写有用的组合函数。此外,它并没有严格遵守:除了良好的意义之外,其他任何东西都不会阻止程序员在某些其他位置返回错误,例如在返回值序列的中间,或者在开始时 - 因此处理错误的代码生成方法是也充满了问题。

在Go中,不可能以任何比一些变化更简洁的方式构成错误的操作

    a, err := fallibleOperationA()
    if err != nil {
        return nil, err
    }

    b, err := fallibleOperationB(a)
    if err != nil {
        return nil, err
    }

    return b, nil

在其他语言中,这可以不同地表达为

    a = fallibleOperationA()
    b = fallibleOperationB(a)
    return b


在有例外的语言中,或作为

  return fallibleOperationA()
        .then(a => fallibleOperationB(a))
        .result()


在具有可以对案例值进行操作的抽象的语言中。

这具有实际影响:执行长序列的错误操作的代码花费大量的打字工作来编写(即使编辑器支持生成分支),并且需要大量的认知努力来阅读。风格指南有所帮助,但混合风格使其变得更糟。考虑:

    a, err := fallibleOperationA()
    if err != nil {
        return nil, err
    }

    if err := fallibleOperationB(a); err != nil {
        return nil, err
    }

    c, err := fallibleOperationC(a)
    if err != nil {
        return nil, err
    }

    fallibleOperationD(a, c)

    return fallibleOperationE()

如果你嵌套它们,上帝会帮助你,或者想要做一些比将错误传回堆栈更有趣的事情。

返回顶部
顶部