"Should have done" is a silly phrase to use here -- Rust is attempting to advance the state of the art, Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language.
Perhaps some day we will see a successor language that follows the philosophy of Go using the new stuff we understand thanks to Rust.
> Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language.
Well-understood like code generators (go generate)? Were generics not well-understood enough, so they decided to go with something that's well-understood to be a poor solution to the problem?
Actually, now that you mention it, I think generics are poorly understood in general. C++, Java, and C# have major differences in their approach to generics, but they have similar syntax so most people gloss over the differences. I think doing generics well requires incorporating higher order types, higher kinded types, and other concepts which don't exist at all in the wild west of C++ templates. This clumsy implementation leads to monstrosities like std::allocator_traits<std::allocator<T>>::rebind_traits<U>, which would be completely unnecessary if C++ had HKT. Java and C# make other trade-offs but also lack HKT and suffer for it.
>I think generics are poorly understood in general
Perhaps by programmers (and the designers/maintainers of certain mainstream languages...), but there are some very strong models out there; we've had well-behaved parametric polymorphism à la Hindley-Milner for decades and Haskell's typeclasses are in my opinion a very well-thought-out approach to ad-hoc polymorphism (and which the more ambitious C++0x version of Concepts in some ways resembled).
C++'s templates are like the untyped lambda calculus of the type-level world; they're perfectly capable of doing most anything you'd want from them, but using them effectively requires a lot of boilerplate and discipline (and as for std::allocator_traits et al., I think that's more an issue of library design than the design of templates).
I've never found subtype polymorphism particularly convincing beyond the case where independent single classes implement meaningful interfaces, and that's basically like a member function–oriented version of typeclasses.
Now, all this said, I haven't actually used Go, so I don't know how well the rest of the language would interact with these ML/Haskell-style systems, but I imagine at least basic parametric polymorphism would be relatively unobstructive.
Concerning std::allocator_traits, it reflects a deeper limitation of the language rather than just a library design flaw. If you had HKT, you could pass std::allocator to a template. You can't do that, you can only pass std::allocator<T> for some concrete T. Equivalently, in Haskell, you can pass IO as a type parameter, so you don't see the same kludges.
I definitely agree with the sentiment about subtype polymorphism.
>I haven't investigated it but variadic template template arguments might help there, though.
They do:
template <template <typename...> class Container, typename T>
Container <T> fin (T n) {
Container <T> set;
for (T i = 0; i < n; ++i)
set.push_back (i);
return set;
}
int main () {
for (int i : fin <std::vector> (10))
std::cout << i << std::endl;
}
You know. As Go get more popular, people will have a critical look at the language and its short comings.That's inevitable.
If the only answer to the issue they raise is "you don't need that in Go", "Use go generate" or "Go isn't for you", Go is going to get a lot of bad rap that will stop the adoption of the language.
If the Go team thinks the language doesn't need generics they are either out of touch(their rights,they don't know owe us anything) or arrogant. Either way, Go will not be the successful language it could have been.
One should never have to resort to interface{} to code anything in Go. Developers want type safety, not write type assertions everywhere. The fact that 1/ Go has generics but you cant create yours(map/slices/arrays) 2/ the core lib is full of these interface{} functions proves the language has a big problem.
If Rust had been missing an important feature like generics, the Servo people would have been telling the compiler people "you're being silly, of course we need generics!". And because Servo is the official test-case project for Rust, the compiler people would have listened.
Go has nothing like this. There's no formal effort to ensure practicality.
It's not about whether someone is using the language, but about whether the language designers decide to listen to a user above themselves.
It's hard to design a language. Everybody is always trying to give their conflicting opinions. Many language designers react by turning inwards and ignoring everybody else.
Generics are maybe poorly understood by the average programmer, but what I meant with my rhetorical question was that there is a lot of literature and there are a lot of people who understand generics very well. Maybe the creators of Go don't understand generics, but then again, maybe if you don't understand generics you shouldn't be developing a high-profile statically-typed programming language.
The Go team might not understand generics, but that doesn't mean generics aren't well-understood.
This is my core problem with Go: they clearly have no idea what a decent programming language would look like and they're getting undeserved attention becauase of their previous (admittedly impressive) work.
I don't think the Go team has any problem understanding anything. I think they have different subjective goals for a programming language than many critics, which leads to a feature set which includes some things critics would leave out and excludes some things critics would prefer (like generics.)
I think Go has gotten fairly decent traction because there are roles for which real programmers find it reasonably well suited compared to existing alternatives, and I think that, not the previous work of the creators, is what drives attention to it.
OTOH, I think Go's period in the sun is going to be a lot shorter than, e.g., Rust's unless we see a Go 2.x that addresses some of the "not in the Go 1.x timeline" things with Go fairly soon.
I do agree that the problem relates to language goals, but their poor choices of goals to pursue do demonstrate a lack of understanding. Their stated goals are frequently solutions that don't really address good use cases. Take for example fast compilation time: the stated use case for that goal was compiling Google's massive C++ code base, which took too long to reasonably compile on a dev machine because of template expansions. Refusing to add generics is an idiotic solution to this problem:
1. Generics aren't templates, and are in fact a solution to this problem. They don't expand at compile time.
2. The long compilation times are indicative of deeper problems: an over complicated code base that should be refactored down, and lack of modularity which would allow partial compilation (only compile the changed modules).
3. Removing generics creates new problems with increased bugs and duplicated code. It's trading compile-time for development time, which has historically rarely been a good trade.
4. Even if this is a legitimate concern for Google (which I still don't think it is), it's not a legitimate concern for 99.9% of projects out there. How many projects actually have millions of lines of code that can't be factored down and modularized? If this is really Go's target then they should admit it's a highly specialized language and not a general-purpose programming language, and that most people shouldn't be using it.
Calling these goals "subjective" as if this justifies them is just a smarter-sounding way of saying "That's just, like, your opinion, man." Opinions can be wrong, and the opinions of the Go team are wrong.
Code generators like RATFOR, lex, yacc, and the C preprocessor? I think it’s fair to say that those are well understood. Generics like Ada Generics, C++ templates, and Java Generics? I think it’s fair to say that we are only beginning to understand the benefits and drawbacks of that approach to code generation, which is why every time I recompile the Java application I’m currently working on, I get erroneous warnings telling my that my varargs are causing heap pollution in my generic static methods. (Which I can suppress, but instead I’m planning to delete those methods.)
Even OCaml, whose approach to parametric polymorphism is worlds simpler than any of the monstrosities mentioned above, just changed its approach by adding GADTs, which I still don’t understand fully despite the best efforts of generous HN commenters.
So I think that, given that Golang/Issue9 decided not to include inheritance, linear types, exceptions, or overloading (even of operators!), in large part because they aren’t well-understood, it’s entirely unsurprising that it decided not to include generics, either.
> Code generators like RATFOR, lex, yacc, and the C preprocessor? I think it’s fair to say that those are well understood.
Yes, that is exactly what I said in the post you are responding to.
> I think it’s fair to say that we are only beginning to understand the benefits and drawbacks of that approach to code generation, which is why every time I recompile the Java application I’m currently working on, I get erroneous warnings telling my that my varargs are causing heap pollution in my generic static methods. (Which I can suppress, but instead I’m planning to delete those methods.)
Just because you don't understand something, doesn't mean that nobody does?
I guess you didn't understand the problem I was describing. I wasn't saying that the problem is that I don't understand parametric polymorphism, or for that matter its realization via code generation, although I’m sure e.g. Philip Wadler understands those things a whole heck of a lot better than I do. The problem was that the Java language designers didn't understand generics, and accidentally implemented varargs in 1.5 in a way that produced a completely unnecessary and avoidable collision with the parametric polymorphism system (designed by Wadler) that they also introduced in 1.5. And there are a variety of similar problems in various parametric-polymorphism systems, which leads me to think that the problem is not merely that some loser at Sun didn't understand these things (and didn't bother to ask Wadler, who surely could have warned him off the particular stupidity I mentioned) but rather that there is a general lack of understanding of parametric polymorphism.
> Go was attempting to use only very-well-understood ideas to make a very-well-understood, simple language.
Many of the ideas in go were not well understood and had only been demonstrated in research languages prior to go. Its concurrency model and associated primitives, and the automatic interface system were in roughly the same state then as Rust's new ideas are now.
That said the ideas in Rust were not as well understood 5 years ago and I doubt that the go creators could have incorporated them well at the time. It took Rust 5 years of experimentation to come to what it is now (and its still got a number of additions to go!).
> Many of the ideas in go were not well understood and had only been demonstrated in research languages prior to go. Its concurrency model and associated primitives, and the automatic interface system were in roughly the same state then as Rust's new ideas are now.
to Go's authors the CSP model was very well understood through three different implementations: Alef, Limbo, and Plan 9's libthread.
Perhaps some day we will see a successor language that follows the philosophy of Go using the new stuff we understand thanks to Rust.