I cant help feeling it is a missed opportunity to add generics to Go this late. A mistake that is copied from earlier languages (C++, Java), a mistake similar to other mistakes Go chose not to solve at it's inception, like: having implicit nulls (C, C++, Java, JS, C#), lack of proper sum types (C, C++, Java, JS) and only one blessed concurrency model (JS).
While I think I get the reasons for these decision in theory, make a simple-to-fully-understand language that compiles blazingly fast, I still feel it's a pity (most) these issues where not addressed.
If you see "unusual problems" with the design, then tell us what they are.
Otherwise it's just shallow pattern matching "Java added generics late, they had problems, Go added generics late therefore they'll have problems too".
Counterexample: C# added generics late and it's perfectly fine design.
The reason Go team is not rushing to implement generics is precisely so that the design makes sense, in the context of Go.
Over the years Ian Taylor wrote several designs for generics, all of which were found to not be good enough.
They are doing it the right way: not shipping until they have a good design and they didn't have good design when Go launched.
If this follows the monomorphic approach described in Featherweight Go [1][2], they will at least avoid problems caused by type erasure and avoid too much runtime overhead.
But then you have compile time overhead (an issue Rust and C++ have faced). One of Go's design goals was to have very fast compile times, which might be in doubt if they take the monomorphization approach.
Is rust's slow compile time because of poor LLVM IR generation or just because monomorphization? D has generics and compiles fast. I guess Nim compile times are okay, too..
Go has the unfortunate circumstances of its birth being before widespread recognition of the value of generics and better type systems. Those ideas existed in many languages, and in the PL community, and they were starting to take hold in other languages, but the consensus for most software engineers was that "sum types" are hard and "generics" aren't necessary and the type system should stay out of the way.
I think TypeScript, Scala, Kotlin, C#, and various others I forget now proved that these things weren't a fad and could yield significant gains in productivity and code correctness.
Had Rob Pike been more forward looking (or hired Anders Hejlsberg or Guy Steele to design the language) or dipped further into the PL research, he might have been so bold himself. I don't think anyone can fault him for it, these were niche and minority views in 2010 and may not even be in the majority today.
I think at the same time, we see what happens when a new language has large corporate backing in more recent years. Swift more closely resembles Rust than Go in terms of its type system.
"Generics are useful" was not even remotely a niche view in 2010. That was already 6 years after Java got them, and the lack of generics in Go was a common criticism from day one.
Philip Wadler introduced generics into Java and had previously designed Haskell, so he must have been thinking about types for at least a further 15 years before Java (20 years before Go).
This is such a bullshit argument. Why is it that any go post on hacker news pulls out all the tropes. Lack of exceptions. Lack of generics. Would generics make go a better a language? Maybe. Does the lack of generics make go objectively bad? Hell no!
I've had a long career coding in C, C++, Java, Lisp, Python, Ruby... you name it I've done it. Go is my favorite most productive language by far for solving typical backend issues. My least favorite? Java by a HUGE mile.
> Why is it that any go post on hacker news pulls out all the tropes. Lack of exceptions. Lack of generics.
It's pretty simple -- those of us who use those feature regularly in other languages know how valuable they are, and we miss them when they aren't there.
Is that really a problem? I think proper sum types to allow Result types that can encode many success/error states are so much nicer than an exception hierarchy. Rust and Kotlin did not go with exceptions, and for good reasons.
> C, C++, Java, Lisp, Python, Ruby... you name it I've done it.
Let me name a few: Rust, Kotlin, OCaml/Reason, Haskell, Elm. These languages carefully selected a set of features, the all have: no implicit nulls and sum types. And in your list non of them have those features. I really wonder what you think of these features when you've worked with them.
Kotlin very much did go with exceptions except for the Result type in coroutines which wraps a Throwable anyway and is only used for the border between the coroutine runner and the async functions.
It was a common criticism but I don't think it was a majority criticism. Hacker News is not representative of the internet at large, and the idea that Go doesn't need or might not even benefit from generics is still pervasive. (See the first person to reply to you.)
If you copy most of your design from Pascal / Modula 2 / Oberon as a safe bet to use a time-proven approach, this is only natural. If you don't want to use a time-proven approach, you need to design your own, and it's a massively more complex and fraught enterprise than adding GC and channels (which are both old, time-proven designs, too, just from elsewhere).
You could say that one could maybe copy the time-proven OCaml. But OCaml doesn't have a proven concurrency story, unlike Oberon and Modula-2 (yes, it had working coroutines back in 1992).
I also wish all these design decisions would not have been made in a new language, like they haven't been made in Rust. Unfortunately, the constraints under which creators of Go operated likely did not allow for such a luxury. As a result, Go is a language with first-class concurrency which one can get a grip of in a weekend, quick to market, and fast to compile. Most "better" languages, like Rust, OCaml, or Haskell, don't have most of these qualities. Go just fills a different segment, and there's a lot of demand in that segment.
>As a result, Go is a language with first-class concurrency which one can get a grip of in a weekend
Which is a mess. Sending mutable objects over channels is anything but "proven concurrency story".
Both Rust and Haskell (and OCaml, if we talk about concurrency and not parallelism) have way better concurrency story than Go. I don't care how fast one could start to write concurrent and parallel code if this code is error prone.
The only difference between Rust/Haskell and Go is that the former force you to learn how to write the correct code, while the latter hides the rocks under the water, letting you hit them in production.
I used "first-class" here to denote "explicitly a feature, a thing you can operate on", like "first-class functions" [1]. I didn't mean to say "best in class". I don't even think we have a definite winner in this area.
Also implicit nulls are not beneficent to anyone. And sum types could have made results (error/success) so much nicer. I see no reason to go with nulls at Go's inception, hence I call it a mistake.
It's easy for spectators / bystanders to call something a mistake because you don't understand the tradeoffs. Try designing and implementing a language and you'll see the tradeoffs more clearly.
> The overlooked thing is rustc produces poor quality LLVM IR which is also mentioned in FAQ.
LLVM is also slow in general. If you use it, it's likely the thing that's bottlenecking your language's compile-time unless you've done something insane like templates and `#include`, etc..
Inevitably it's the case that even if your source language doesn't do nearly as badly at the design stage when it comes to generics as C++ does, if you use LLVM your build stage is probably going to be unacceptably slow.
Agreed. But so is GCC. And I guess many of the 'zero cost' abstractions require some advanced optimizing compiler like LLVM to be zero cost (or move that complexity to compiler end).
They specifically mentioned the technical debt and poor LLVM IR Generation issue though. I wonder if it has yet gotten attention or fixed. Maybe @pcwalton knows.
Go and D made the tradeoff. And both have good compile times. Both of them have GCC and LLVM frontends as well as a homegrown one. And homegrown ones have fast compile times.
Edit: well fast compile times at the expense of optimization. But that kind of proves the point.
I feel the same way, but then again Rust (among others) exists so it's not like those of us who dislike this approach are "stuck" with Go. I think it's actually nice to have the choice, reading the comment in this thread it's pretty clear that there are people who don't feel like we do.
Go clearly values "simple and practical" over "elegant". It seems to be quite successful at that.
I have to refute this 'simple and practical' claim.
Not having generics and neither having very common tools doesn't seem very good. You will have to write a for loop for what is a simple function call in python or javascript or <insert modern language here>. Such detail easily interrupts reading / writing flow.
I agree with the spirit of what you are saying, but I'd nit pick and say that lack of generics and presence of implicit null types make Go not simple and not practical over other options in the same space.
As someone using Java which has null and hardly using any generics even though they are available in Java. I find Java immensely practical with huge number of libraries and other facilities of ecosystem.
Seems you are of the opinion if you do not find something practical no one else can.
And I find go immensely practical and Java immensely impractical. But I see its value. It's almost like we have different languages because people are myriad :)
Problem-space and learning styles play a huge role.
Having seen Java shortcomings up and close I can totally understand that. It is just that some folks think their subjective opinion about programing are some universal objective truths.
Incidentally, Java and C# have addressed (or are in the process of addressing) both issues. Both languages/platforms are superior to golang in almost every conceivable way.
I've used a lot of Java and C#, and a decent amount of Go. I'm not sure I would call Go inferior. The design goals were different. I'm also not a language wonk, so maybe that's why I enjoy the relative simplicity of Go. The developer loop of code, run, code is very fast, and the standard library is good out the box. I just want to write code and get stuff done. To that end, Go is another capable, workman type language (like Java or C#).
A form of pattern matching and switch expression has already made it to the language as of JDK 14. Those are paving the way for full blown pattern matching and sealed types:
To be fair, Swift have a bit strange generics implementation that forces programmer to jumps through hoops to achieve something quite common in similar programming languages. The whole standard library is peppered with AnyThis and AnyThat, the language doesn't have generators (yield) and I'm not sure they're possible with the current generics design and programmers needs to learn what is type erasure just because the core team decided that returning generic interface from a function is something nobody will want.
I like Swift a lot, for many reasons, but generics design isn't one of these reasons.
Fair 'nuff. I was always a fan of the way C++ did it, but it was a great deal more technical.
Swift is sort of "generics for the masses." C++ is a lot more powerful, but also a lot more "in your face." I really do like the way that Swift allows generics to have implicit types.
While I think I get the reasons for these decision in theory, make a simple-to-fully-understand language that compiles blazingly fast, I still feel it's a pity (most) these issues where not addressed.