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.
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.
1. People will eventually want generics 2. Retrofitting generics onto an existing language is hard and leads to unusual problems
(edit: I'm glad Go is doing this, but...Java learned this in 2004.)
1. https://arxiv.org/abs/2005.11710 2. https://news.ycombinator.com/item?id=23368453
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.
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.
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.
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.
We all called this when Go was created, too.
(Not to disparage WASM, which has some nice ideas both on the technical and ecosystem level.)
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.
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.
That's an overstatement.
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.
Generics and Compile-Time in Rust
https://news.ycombinator.com/item?id=23534974
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.
And generics reduce amount of manual for loop juggling code one has to write. I don't think adding generics makes much of a difference.
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.
Edit: well fast compile times at the expense of optimization. But that kind of proves the point.
Go clearly values "simple and practical" over "elegant". It seems to be quite successful at that.
Seems you are of the opinion if you do not find something practical no one else can.
Problem-space and learning styles play a huge role.
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.
> lack of proper sum types (C, C++, Java, JS)
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.
In any case, there already exist solutions in place:
Optional<Integer> foo;
//....
if (foo != null) {
foo.and_then(new Consumer () {
function accept(Integer foo) {
}
});
}To ensure assignment before use make it final.
It's been a WIP for years. Lets judge based on status quo rather than being disingenious.
I'm assuming you're talking about goroutines aka virtual threads/fibers whatever which are entirely different from actual threads.
EAP build - http://jdk.java.net/loom/
I think that most of the standard language features are based on generics, but you would never know it, as a user of those features.
I like Swift a lot, for many reasons, but generics design isn't one of these reasons.
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.