What I don't like is Go

  • Transfer
At the current job have to use Go. I am well acquainted with this language. I don't like him, and his popularity puzzles me.

Ergonomics of development

I have never met a language so openly opposing the convenience for the developer. For example, Rob Pike has repeatedly and openly hostile to any discussion of syntax highlighting on the Go Playground . In response to reasonably formulated user questions, his public answers sparkled with disdain and disrespect:

Gofmt was written specifically to reduce meaningless discussions about code formatting, which was great. Unfortunately, this did not affect the number of meaningless discussions about syntax highlighting or, as I prefer to call it, spitzensparken blinkelichtzen.

And again in the 2012 Go-Nuts branch :

Syntax highlighting - for small ones. In childhood I was taught arithmetic on colored sticks . Now I have grown up and use black and white numbers.

Clearly, of Rob’s acquaintances, no one suffers from synesthesia, dyslexia, or poor vision. Because of its position, the official Go site and documentation are still without syntax highlighting.

The Go development team is not limited to Pike, but the others strongly support his attitude to ergonomics. In the discussion of union / sum types , the ianlancetaylor user rejected a request specifically defining the advantage of ergonomics as being too insignificant and not worthy of attention:

This has been discussed several times in the past, including before the open release. Then we came to the conclusion that sum types do not particularly extend the interface types. If you look, in the end it all comes down to the fact that the compiler checks that you have filled in all cases of type switching. This is a pretty minor advantage to change the language.

This attitude is at odds with the opinion of union types in other languages. In 2000, JWZ criticized Java:

I also think that quite lamer idioms are used to model enum and: keywords. (For example, the compiler does not have the ability to issue a saving warning that “ `enumeration value x'is not processed in the switch”).

The Java team took such criticism to heart, and now Java can issue this warning to multiple-choice operators for enumeration types. Other languages ​​- including modern languages ​​such as Rust, Scala, Elixir and the like, as well as Go's own direct ancestor of the C language - also issue warnings where possible. Obviously, such warnings are useful, but for the Go team, developer comfort is not important enough and is not worth considering.


No, I'm not talking about intrigue in mailing lists and at meetings. The question is deeper and more interesting.

Like any language, Go is a policy tool. It embodies a specific set of views, as software should be written and organized. In the case of Go, an extremely rigid caste hierarchy of “experienced programmers” and “unqualified programmers” imposed by the language itself is embodied.

On the part of unqualified programmers, the language prohibits functions that are considered “too advanced”. There are no generic generics here, you cannot write higher-order functions that generalize more than one specific type, and extremely strict rules about the presence of commas, unused characters and other shortcomings that may occur in normal code. Go programmers live in a world that is even more limited than Java 1.4.

Experienced programmers are trusted with these functions - and they can give systems on such code to colleagues on both sides of the barrier. The implementation of the language contains generic functions that cannot be used in practice, and with type relations that the language is simply not capable of expressing. This is the world in which Go programmers live .

I don’t know how inside Google is, but outside of it, this unwritten political separation of programmers into “trustworthy” and “unreliable” is at the core of many arguments about language.

Packages and code distribution

The package manager is go getfrustrating with its disclaimer. Packet boundaries are a place for communication between developers, and the Go team basically refuses to help.

I can respect the position of the Go team, which is that this is not their problem, but here their actions are unfavorably different from other main languages. Suffice it to recall the catastrophic history of trying to manage packages for C libraries and look at Autotools - an example of how long such a disastrous situation can last. With this in mind, it is quite surprising to see that the language development team in the 21st century washes their hands in such a situation.


A monolithic way for all sources inevitably leads to version conflicts between dependencies. The configuration vendorpartially solves this problem at the expense of a substantial repository swelling and a nontrivial change in the connections, which can lead to errors if there are references to the vendor and non-vendor copies of the same library in one application.

Again, Go’s “not our problem” response is frustrating and frustrating.

Error handling in Go

The standard Go approach to actions that may end in an error includes returning several values ​​(not a multi-component object; in Go there are none) with the type of the last value erroras an interface, where value nilmeans “no error”.

Since this is a tacit agreement, it is not represented in the Go type system. There is no generic type representing the result of a potentially erroneous operation on which useful unifying functions can be written. Moreover, it is not always respected: nothing but common sense prevents the programmer from returning it errorin some other form, for example, in the middle of a sequence of returned values ​​or at the beginning — therefore, error handling methods are also fraught with problems.

In Go, it is impossible to compile potentially erroneous operations in a more concise way than something like this:

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

In other languages ​​this can be stated as

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

in languages ​​with exceptions or

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

in languages ​​with corresponding abstractions.

This is a significant difference, especially if you have long sequences of such operations (even with the support of an editor that supports branch generation). We have to spend extra time on coding and additional cognitive efforts to read the code. Style guides help, but mixing styles only makes the situation worse. As an example:

a, err := fallibleOperationA()
if err != nil {
    returnnil, err
if err := fallibleOperationB(a); err != nil {
    returnnil, err
c, err := fallibleOperationC(a)
if err != nil {
    returnnil, err
fallibleOperationD(a, c)
return fallibleOperationE()

God help you to make an attachment or something besides sending the error back to the stack.

Also popular now: