Go Is Unapologetically Flawed, Here’s Why We Use It
bravenewgeek.com
bravenewgeek.com
From Java 7 onwards files can be read with a single method call. Look at the static methods on the Files class.
I find the article to be poor. He says he likes Go, and then proceeds to illustrate why nearly all the features of it are dubious. Then he admits that any article that criticises Go had to be prefixed with his disclaimer because otherwise the community will get nasty, so does he really like Go? Hard to say. His employer requires him to use it so it's not like he can openly say he hates it.
Finally he says Java is ghastly, but his only evidence for this is he doesn't like one of the frameworks built on it ... a framework that Go has no equivalent to anyway. Ok... so don't use j2ee then. Use something like Play or Ninja. He would then benefit from good GC and working debuggers/CPU profilers.
I think with 'go' as a language when you start with it, there's a lot to love: things are simple again; "Look ma, a real program that does something with almost no code and also no magic shit behind the scenes!". But things are always simple and nice in the beginning, when you are focusing on just a small part of the complete picture.
The problem with 'simple' is that unless it abstracts away all complexity you'll ever need to deal with, you'll need to do a lot of work yourself: it's the typical RISC/CISC argument from yesteryear: do you want a small instruction set which is simple but results in a longer program, or a complex large instruction set which results in a smaller program?
So, to be able to deal with all possible complexity in all possible scenarios, either the language has to be vast in its expressiveness (go isn't) or it has to come with a vast standard library which offers additional expressiveness through classes / methods / functions / subsystems or whatever the language is lacking, OR you have to write it all yourself.
In the end, the net-result is the same: the system as the end result of your work is the combined complexity of the code written by the developer(s) and the complexity of the libraries used. Go doesn't change that, it just moves the divider between 'own code' and 'libraries' more towards 'own code', even if that's simpler to write because the language lacks things which some people might consider 'oh god this is complex, I'll never understand this'.
One nitpick about his comment about sending to closed channels. This is indeed programmer error and SHOULD panic, in my opinion. It is clear the author has not read up on the semantics of channels. Channels should only be closed by the sender. If you have multiple senders, how on earth would it be a valid situation for one of these senders to close the channel if the other sender still has data to send? You're going to need some form of synchronisation mechanism for this.
Also, if semantics make life complicated, they should be changed. Since there's already an 'ok' return flag for channel receive, I have hard time understanding why there can't be one for channel send.
I was suprised to read that they actually consider removing the sync/atomic packages. It's crucial in my opinion, and would complicate a lot of code if had to be written with channels.
I agree that generics are very useful when writing reusable libraries, but I disagree they are absolutely necessary when writing and maintaining software at large scale. At large scale, at some point, processes communicate with other processes on the same machine or over the network, and you have to marshal/unmarshal and type check at runtime.
Your example with the function isInt64InSlice is a good illustration of why the lack of generics is annoying at small scale, but it hardly shows why this is a problem at large scale.
PostgreSQL is an example of a complex piece of software, written in C, which doesn't use generics and is still very readable and maintainable.
A few other technical points:
- "Cannot add methods to types from a different package": Would you want something like extension methods in C#?
- "Channels are slow": I've read there is some work going on to improve them. That said, you admit yourself that Go is still one the best option among the "concurrent" languages. Are you aware of another language with a similar mechanism builtin in the language or the library and that performs better on this matter?
- "Accidental implementation of an interface": I agree this a problem in theory, but in practice it's very rarely an issue. Have you been beaten by that often? Do you think the benefits of implicit implementation are not worth it?
- "Cannot implement your own exceptions": Yes you can. You can pass some value to panic and retrieve it upper in the call stack with recover and decide what to do depending on the value. It's not something Go developers do often, but there is some use of this technique in the standard library for example. It's explained in "Effective Go".
- "Map returns a bool instead of an error": I don't see the issue here. A lot of other languages do the same and return a default value and/or a boolean at false when you probe a map with a missing value.
- "Adding an item to a closed channel panics": This is a programming error, like a division by zero or an out of bounds array access.
- "Channels as iterators": Goroutines and channels are not designed to build iterators. They offer a general low-level mechanism for concurrency. How would you expect the goroutine to know that nobody is listening anymore and shutdown?
About the community, I would not call it "stubborn". In my experience, it is generally helpful and very professional. But I agree that the topic of generics has become a taboo, which I regret. It looks like the core team wants to focus on improving the compiler, the runtime, the library, the tooling and the garbage collector, and because of that wants to keep the language stable in the meantime. I understand the frustration, but don't you think their decision makes sense and is the best way to use the limited resources of the core team?
You wrote that "a language can have considerable depth while still retaining its simplicity". What languages would you recommend that solve the flaws of Go while preserving its simplicity (in terms of user experience)? It's a sincere question. I'd like to use such a language.
More generally, you ask how can we make developers more productive and how can we enable them to solve problems? That's interesting because this is precisely the question Go tries to answer. Are you sure the answer lies in a more powerful programming language? What about the runtime, the tooling, the libraries?
The Go team thinks the answer lies in simplicity (light syntax, garbage collection, interfaces, composition over inheritance, builtin arrays/slices/maps), builtin concurrency (goroutines, channels), great tooling (speed of compilation, go test, gofmt, godoc), easy deployment (no virtual machine, static binary).
Do you think the answer lies in having type parametricity, algebraic data types, pattern matching, immutability, Hindley-Milner type inference, higher kinder types, etc.? I'm not saying they are not useful or desirable. I'm just saying that the low hanging fruits in terms of developer productivity may be elsewhere.