Why Go Is Not Good (2014)
yager.io
yager.io
My goal was to derail the Go hype train that dominated HN at the time, and I think I was pretty successful. As you say, everyone is pretty aware of Go’s shortcomings by now.
It says Go’s solution to generics is interface{}. Not really; Go just doesn’t have a solution for that yet. Go 2 of course, promises to add one, that would answer to the basic asks in this article.
As for operator overloading and language extensibility, I think most people would agree these are never really clear cut wins as they can add inordinate complexity in exchange for sometimes dubious value. It certainly makes some things look nicer (matrix and vector types for sure) but you can always use functions to get the same utility with less sugar.
You can go on. A lot of concerns raised about Go don’t end up reflecting the day to day problems with writing Go code though, which imo is a sign that there is a gap between what people think is a problem by comparing it to other languages like C++ and Rust, and what is actually a problem by experiencing the language first hand for an extended period. I’d argue the generics problem is overblown; a solution to it will probably be welcomed, but it wasn’t like Go codebases were suffering from this. It was just making certain things (like working with slices) slightly more awkward and error prone.
For an article from 2014 these would’ve been valid concerns though in practice nobody writes Go code the way that they write C++, Rust, etc. code making some of the complaints feel like they are missing the point.
I wrote this like 7 or 8 years ago, so it was fairly novel at the time :)
> Go just doesn’t have a solution for that yet. Go 2 of course, promises to add one
I’ve also been hearing this since I initially wrote this. In any case, the article is about Go as it exists. If Go 2 actually makes significant improvements in these domains I will add an updated header pointing that out.
However the Go 2 thing is not in the same state it was in 2014; the design proposal was formally accepted and a compiler exists. It will likely take a long time to see how it hashes out, but it’s probably fair to treat it as an inevitability at this point.
Operator overloading to do what C++ did from the outset is a mistake. It looks flashy, but it buries land mines in the habits of your programmers. A programmer who is actually both shifting numbers and doing some other arithmetic operation may look at the code and wonder, "What is the precedence of these operations?" and having concluded that they don't remember, or at least, that their reviewers may not remember, though the compiler surely does, they'll add parentheses or re-factor. But if they've learned that shift is really some other operation concealed by overloading - they can be astonished when the compiler cheerfully applies that other operation with the precedence of a shift 'cos as far as the compiler is concerned that's still what it is.
But there definitely are uses (matrix addition for example) where just overloading an operator feels very sensible. I can see a strong case for == and != too if your language has that from the outset, it won't make sense to add it later.
So the trick is that a language should do this very sparingly and encourage programmers likewise, but I think that only makes it a feature to avoid entirely if you're very dedicated to having a small language, since overloading is nice but not necessary. Go maybe is in that category.
- Reasonable fast compiler with helpful error messages
- Out of the box cross compilation
- Produces self-contained executable files by default (statically linked binaries)
- Forward compatibility: code written today will (almost) always compile with the latest compiler version from the future.
I haven't experienced any other language that does this.
(Maybe zig will get there soon, but it hasn't reached version 1.0 yet)
Also as a language, it provides a set of really important features:
- Value semantics for structs
- Distinction between values and pointers
- Raw access to byte buffers (most other languages try to conceal them)
While the mandatory GC means you can never be 100% in control of performance, the above mentioned points get you ~60-80% of the way there.
Isn't this backwards compatibility? Forwards compatibility would be something like older compilers working with features introduced in later compilers.
Lazarus/Pascal does all of that. There are many others, Go isn't unique.
The "perpetual reposting" seems like a signal that it resonates with some people? Once again proving that it likely isn't meaningless (at least to everybody).
discussion from 2015 with 356 comments: https://news.ycombinator.com/item?id=7962345
These criticisms of Go are basically correct, but it does have some advantages over Rust, like in some situations absolute performance is not so important, copying strings around a lot is okay, and dealing with the borrow checker is extra needless work.
Go is the language you design when you have absolute conviction that whenever you feel forced to write complex code, you need to rethink your solution instead. It is not a language you would design if you were doing business application programming and were forced to model unbounded amounts of complexity beyond your control. (Java admittedly didn't originate that way, but it sure looks like it did.)
Gosling was obviously inspired by C++ ("without the guns, clubs and knives", as he said), which itself adds a lot of the complexity to support large-scale projects, features that C was lacking. I'm not entirely sure he thought about it much past that.
How can someone claim that kind of thing without being joking is beyond me... Go might have some platform features that may be better than Java (i.e. crosscompilation to native on every OS) but as a language?? What exactly does it do better???
I don't really like how the author just chooses some features which Go doesn't have, and based on that says "Go doesn't bring anything new".
Yes, Go lacks some features, for better or worse, but that's not really the right conclusion.
One feature Go does really well and imo more ergonomic than any other widely used language is asynchronicity. There's no function coloring and all functions are asynchronous.
It's really freeing to be able to spawn almost free asynchronous tasks anywhere, and having no sync-async divide in the ecosystem.
I really miss this in other languages, even if I understand why some (Rust) didn't choose that approach.
There's another form of function coloring, "does this function take context or not".
- It's easy to call function-without-context from function-with-context (but it breaks cancellation)
- It's hard to call function-with-context from function-without-context (you must create context first)
- Some functions in standard library have context, and some don't
> To Go's credit, it is idiomatically correct (and encouraged) to leverage Go's multiple return mechanism to return a second "failure" value if there is a possibility that a function will fail. However, this mechanism can easily be ignored or misused
A few paragraphs above you wrote this in defence of Rust's approach:
> No matter the programming language, people can always write poorly-named functions.
So for Go, user error isn't an acceptable defence, but it is for Haskell and Rust?
I think you made a lot of valid points, but this one seemed contradictory, unless I've missed the point. Apologies if so!
I’m not sure I would agree that poor naming is even the same category of problem as ignoring error return values.
Haskell/Rust provide much more robust error-return mechanisms that can’t simply be ignored or forgotten.
This is often a pointed missed by people that don't like type inference.
Very often, you care very little about the actual type information that comes back. What you care about is "does this have field xyz" or "Does this contain a iterable of data with field xyz". You don't care if that's some specific type of list or what package that data is contained within.
By heavily using type inference, you make it so future changes to where the code lives or even the implementation of that code requires fewer ritual changes throughout a codebase. I don't have to go to a bunch of import locations and change foo.xyz to bar.xyz.
It reduces the friction caused by static typing that dynamic type people like to harp on.
In general, the only potential downside is a loss of readability. That, IMO, isn't a terribly strong argument. Usually you'll have an IDE that will tell you the types and the people that don't like type inference are usually pretty silent on chained operations (foo().bar().baz()) which technically have the same theoretical downside.
How often does this really happen in practice? You know the software doesn't work when you try a wrong type, because it blows up on startup or on a request. In my experience my bugs aren't because of problems with types but with logic errors. It compiles but is not correct because you're doing the wrong thing. You know pretty quickly in Python or Javascript when you make a mistake because you pass in the wrong arguments and the right thing _doesn't_ happen.
Types don't save you from typing the wrong code or simply doing the wrong thing in a type safe way.
In the last few years I've seen so many blog post complaining about go, like this one (from 2014, btw) and this [0] one; both discussed quite often on HN. And yet, I only find more and more projects using Go. The language surely has drawbacks and I personally don't like it, but it clearly has fans and is quite useful to some people.
[0] https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-...
As a language it's not really used for doing dot product on a matrix, whereas Scala for example, there's a pretty clear case for operator overloading because it's more geared towards complex numerical cases where that might make more sense
And having to type tons of boilerplate for any trivial task isn't "expressive simplicity".
Why Go Is Not Good (2014) - https://news.ycombinator.com/item?id=10704115 - Dec 2015 (453 comments)
Why Go Is Not Good - https://news.ycombinator.com/item?id=7962345 - June 2014 (356 comments)
Rob and the other plan 9 folks hated C++ and Java with a passion because both took C and made it "complicated". They wanted something you could write servers, clients, and operating systems in.
Go does not have inheritance.
Also, the worst abuses of inheritance I've seen were all from supposedly senior engineers. And inheritance seems to be attacked a lot lately for questionable reasons. There are many places where it's a natural fit.
Myself, I don't consider inheritance to be problematic when used wisely. But one shouldn't expect inexperienced programmers to act wisely.
My main point, though: Don't act like the inheritance question has a consensus answer. It doesn't.
A good alternative is composition, but you have to have a clear view of what you want. I alway try to make one function/method per action (when i have more then three indent level in a function, i'm prototyping), and that helps more than inheritance for code reuse (and i avoid comments inside functions). And i use a lot of interfaces and "default methods" to emulate "traits" which is imo superior to inheritance, in a lot of way.
I still dislike java and groovy, when you spend hours depiling constructs to understand how to make a minor change on data collection, this is not a good language.
I still find the adoption of Go interesting though, considering how limiting Pascal/Modula/Oberon felt to me when leaving them behind.
: with real capturing closures and garbage collection, as in Scheme
— Rob Pike <http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fro...>
> The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
– Rob Pike
[1] https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fr...
[2] http://nomad.uk.net/articles/why-gos-design-is-a-disservice-...
The reason why the toolchain rocks, the language is simple, and builds are lightning fast are because of these tradeoffs for the most part.
The problem I have seen with many other programming languages is that as they try really hard to become a "good language" they eventually always become a "bad language" because of all the changes which they implemented in order to become "good".
Picking on Go at this point is poor sport. It's been around for a while and everyone realizes its moronic and deficient. It works for Google, though.
Why is the post flagged, by the way? Seems okay to me
I don’t put anything on my blog unless I think it has a pretty long contextual lifetime.