75 comments

[ 5.3 ms ] story [ 147 ms ] thread
Is there something here that hasn't been said before? From skimming it just looks like the usual list of complaints we've seen a thousand times
I appreciate that they've done a good job of describing why things like a lack of generics hurts and alternatives, but ... yeah, this is the beaten horse.
Just another field for battle in the Go vs Rust HN comments war
I wrote this 7 or 8 years ago, so it was pretty novel at the time :)

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.

Go was designed to not cause widespread damage in the hands of a mediocre programmer. It prevents people from being too clever at the expense of more boilerplate and general busywork. This is the exact tradeoff that google wanted to make.
(comment deleted)
I've heard this repeated several times. Is there a source for this claim from any of the people who designed the language?
> 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 <http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fro...>

From [1] via [2]:

> 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-...

(comment deleted)
(comment deleted)
If that was the goal then why not just use Java? It fits this goal description even better and offer superior tooling, infrastructure and mindshare.
One important reason: Because Java encourages inhertance-based designs, which are especially dangerous if your goal is to get inexperienced programmers to write simple, straightforward code.

Go does not have inheritance.

But inheritance is thought to be a key to software reuse, and reusing the existing software is the safest path for inexperienced programmers to become productive.
Inheritance is thought by some to be a key to software reuse. By others it is considered a huge mistake.

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.

(comment deleted)
If this was true, it would be an argument that applies just as powerfully to Rust. Ironically: it's the argument you'd expect Java developers to deploy.
Well... Inheritance have some use, sure. But productive code is code that is easily refactrorable, uncomplicated and easy to read. Inheriting, or rather subclassing too much will kill you code base (i'm currenctly working in a bank, writing mostly java and groovy, i think everybody agree except managment).

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.

But it does have goroutines and many other features which are easy to make a mess with.

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.

True, but I would argue that a novice/mediocre programmer will tread more carefully when using goroutines because the subject itself is often taught as a more complex/high level topic. Meanwhile, inheritance is often taught as a basic concept which can deceive inexperienced programmers into thinking that they completely understand it. That invites more potential missteps.
the glib but mostly correct answer is "because rob and others didn't like public static void main."

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.

I think of Go as a lovechild of C and Scheme, mainly; I wonder if there's a citation for the claim made about design for preventing damaging programmers.
You could also assume it as a rip-off of Modula/Oberon.
As is Acme, a text editor. Pike is like that.
Interesting, that was new to me. Wirth's work is not the worst source for inspiration of course.

I still find the adoption of Go interesting though, considering how limiting Pascal/Modula/Oberon felt to me when leaving them behind.

It might be because Go is a more defined C variant () but with a very full standard library.

: with real capturing closures and garbage collection, as in Scheme

(comment deleted)
Yes, it's not "good" in general terms but it's a lot better than Python or Ruby, and that's still a win.
Looks like Rust is the "good" solution for all of these?
> "I like Go. I use it for a number of things (including this blog, at the time of writing). Go is useful. With that said, Go is not a good language. It's not bad; it's just not good."

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".

A.k.a, "the road to hell is paved with good intentions."
Needs a "2014" note in the title.
Blogs without timestamps ought to be illegal.
Also, it might be useful for HN to require submitters to provide a year for the URL they are submitting. The year is automatically appended to the title as ($YEAR) unless it's the current year.
Sorry! It’s in the HTML. That said, nothing about the blog post has become outdated AFAIK, which makes my points a bit stronger if anything...

I don’t put anything on my blog unless I think it has a pretty long contextual lifetime.

The point of `auto` and `:=` is not to "shave off a few seconds of effort manually looking up the return type of bar(), and a few characters typing out the type in foo's declaration.", but to help with refactoring.
Yup.

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.

What's it with the perpetual reposting of meaningless rants like that? https://news.ycombinator.com/from?site=yager.io
Why is it meaningless? It sounds like you disagree with it, so maybe you would like to discredit it's face value.

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).

At the time of writing, Go was extremely overhyped on HN. I wanted to cool off the excitement a bit. These days everyone is aware of the points I made, but back then this was a refreshing take.
> You might be expecting a linked list of integers, but at some point, some tired, coffee-fueled programmer with a deadline to meet accidentally put a string in there somewhere

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.

Go is like a superior version of Java. Rust is like a superior version of C++. I wouldn't use Java or C++ any more for a new project, barring some very specific reason. But I might use either Go or Rust depending on the project's priorities.

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 like a superior version of Java

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 think I would classify Go with Java. I would not use Go to write a system with a rich domain model and lots of business logic. Java, in its own dated and awful way, is designed to cope with complexity. Go is designed to punish it.

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.)

It is odd that Java was conceived for set-top boxes. Even before generics were added, it was clearly a language aimed at establishing and enforcing contracts in a largish team over long development cycles. You can see it in the Hello, World program, which has a lot of weight you wouldn't need for space and time constrained programs on a set-top box.

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.

(comment deleted)
Go may not be the best as a language, but it's the best in terms of toolchain.

- 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.

>I haven't experienced any other language that does this.

Lazarus/Pascal does all of that. There are many others, Go isn't unique.

Rust is pretty darn close. Compiler definitely not as fast, but error messages are much better. Given the choice between rust and go, I have chosen rust; but could just as easily have gone the other way!
Having a slow compilation time makes it no where close.
> Forward compatibility: code written today will (almost) always compile with the latest compiler version from the future.

Isn't this backwards compatibility? Forwards compatibility would be something like older compilers working with features introduced in later compilers.

> · Go doesn't really do anything new. · Go isn't well-designed from the ground up. · Go is a regression from other modern programming languages.

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.

Even at the time, other languages like Erlang and Haskell had similar or superior green threading. These days there are many languages with decent green threading. I know you used the hedge “widely used language”, but that’s cheating because any language Google sponsors will be widely used :)
> There's no function coloring and all functions are asynchronous.

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

This seems odd for an article that got 30 upvotes in 15 minutes (probably not really enough time to read it all, mind you.) The complaints are a rehash of what’s been said already, only worse. Which is completely fair considering it is at least as old as 2014!

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.

> The complaints are a rehash of what’s been said already

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.

Yes, to be clear, it makes sense for back then. It was weird to see it on HN frontpage as if it were new.

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.

> 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.

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.

There are two types of languages: Those that people complain about and those that nobody uses.

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-...

(comment deleted)
For the n-th time: Go was designed for simplicity. Think of Go as Python, but with static typing, better concurrency and performance. Software is complex enough as it is, there's no need to make it harder by introducing concepts that only clutter your perception and have questionable positive impact on development. Trying to be clever when designing a new language is easy, achieving expressive simplicity is hard.
Python has operator overloading and is generally pretty complex language. It's better to think of Go as C, but with greenthreads and GC.

And having to type tons of boilerplate for any trivial task isn't "expressive simplicity".

You wrote this, about the potential for user error with a Go example, as a negative:

> 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!

Not OP, but original author.

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.

Fair point, misunderstanding on my part, suspected it was, thanks for clarifying.
The way I see it is Go was made as a thin layer on top of C to bring safety, modern toolchain, and concurrency.

The reason why the toolchain rocks, the language is simple, and builds are lightning fast are because of these tradeoffs for the most part.

Reading the criticisms I thought to myself "maybe I'll stick with C#".

Why is the post flagged, by the way? Seems okay to me

How does this article submission get flagged?

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.

(comment deleted)
Another point, Go has always been pitched as a simple, what you see is what you get, general purpose language. The case for re-defining the meaning of a '+' symbol, for example, is a conscious decision to ensure that the language remains as explicit as possible, as you alluded to yourself.

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

Using a freshly created throwaway to post some old criticism about Go. Says a lot.