But, also, number one liked issue: simplicity. The go team doesn't say they don't want generics, they say they haven't found a non-complicated way to implement generics.
There are just the N ways PL research has found for implementing generics. Either they implement them, and tackle the complication one of those N ways entails, or they don't.
Besides, has anyone seen much experiments, with actual coding or at least design, for finding this "non-complicated" way anyway? Not just some post once in a blue moon re-iterating the basic well known tradeoffs.
Yeah, I've read both -- and more. None of this is what I described: either actual code, even if in some experimental compiler branch/fork, or at least a concrete design.
Ian's just reiterates the various options (Rob Pike has already written similar things), and Russ Cox is just several vague ideas for future additions to the language.
So, Russ says it is Ok to have generics but there are higher priorities. The problem with this approach is that eventually when they will add generics, there will be whole ecosystem living without them, which means this ecosystem will need to be entirely reimplemented.
Yet millions of VB and C# users deal with them every day and seem to be doing fine. For a concept that's so old, it seems unlikely they're going to find some breakthrough either in implementation or language-simplicity.
Add invariant-only generic types (no subtyping relations between types with different type parameters) as a way to avoid 90% of casts. It doesn't get easier than this.
As for going further, I think covariance and contravariance, while conceptually simple, will always be too complicated to wrap your head around.
I think the future is invariant types + advanced generics functions. But research hasn't explored that direction yet. I might want to do that in the future.
If it's relevant to anyone's interest, feel free to contact me.
It's not possible to sum them. See the explanation of how to read the data under the "What do you like about Go?" results. In particular, "management" and "dependency" both include the "dependency management" counts, so including those in the sum double-counts responses that say "dependency management".
I ran the numbers just now, and 571 responses contain the (case-insensitive) text substring "package manage" or "dependenc", compared to 623 for substring "generic". These two topics are very clearly the top two. They're also both on my list for this year (research.swtch.com/go2017). It doesn't seem that important which one "won".
If you have suggestions for how to summarize free-response text in a way that lets the data speak rather than lend itself to cherry-picking, please let us know. I'm certainly open to improved analysis next time.
The interpretation of the open questions could definitely use some semantic analysis; I agree that it really does look like there's more frustration around packaging than generics, based on how they interpreted the results.
And one could say it was too early. Under the hood, generics are pretty bad in Java [0]. Type information is mostly lost; it's a compiler time only feature. They're much better implemented in C#, even if they look the same on the surface, probably because it came later and they took it seriously.
So I applaud the Go architects with being conservative about it. More languages should take their time thinking about implementing features, no matter how needed they seem to be initially. You only get one chance, and then if it's a bad choice you live with the consequences.
Just about every programming language expert I've seen who's talked about Java generics says Java did a better job than C#. In fact, of all language runtimes that do generics, the .NET runtime is the only one that specializes. Examples of runtimes that do not specialize or reify generics:
* JVM
* Haskell runtime
* ML family of languages
* Modula-3
* Ada
The problem with specialized generics in the runtime is that they bake the type system of the language into the runtime itself, making the runtime more difficult to work with. Type erasure, for instance, makes it much easier to implement alternative languages on the JVM such as JRuby, Clojure, Scala, or Kotlin, than on .NET.
As another example, C# in 4.0 added the concept of generic type variance, which is completely at odds with specialized generics. To implement this feature, the C# language designers had to modify the underlying runtime itself and add special bytecode instructions for the feature. The feature itself is also incompatible with specialized generics, so when you use it you silently lose specialization.
In contrast, thanks to type erasure, the Scala language designers could easily add the same feature on top of the JVM as-is without needing Oracle to add special bytecode instructions.
Specialization has advantages for performance when doing number crunching with data structures and algorithms operating directly on integral or floating point data types. At the end of the day, though, if you really need performance for this very narrow field of use cases, you're probably not going to be using generic data structures and algorithms anyways. It takes an incredible amount of effort to build a data structure or algorithm that is both generic and also performant in all possible usage scenarios. It is however, much easier to build a data structure or algorithm that is specific and performant relative to your use case alone.
Case in point: when I was in college doing a NLP assignment that needed to be fast, my data structures were arrays of doubles because that was the easiest way to reason about and ensure performance.
> In fact, of all language runtimes that do generics, the .NET runtime is the only one that specializes.
As a C++ programmer I would mention templates as something similar.
> The problem with specialized generics in the runtime is that they bake the type system of the language into the runtime itself, making the runtime more difficult to work with.
When inheritance comes in to play Java ends up storing generic type information in the child classes. It even has to generate bridge methods since Runtime does not really deal with int compareTo( Comparable<? extends T> ) vs. int compareTo ( MyInteger ) .
> if you really need performance for this very narrow field of use cases, you're probably not going to be using generic data structures and algorithms anyways
An ArrayList<Integer> makes sense in very few cases and is extremely slow, 64 bit pointers to objects that contain useless metadata and a immutable 32 bit int. Sadly an IntList is neither generic nor is it compatible with anything that operates on List<T>s, which results in duplication of a lot of algorithms just to sort primitives.
> when I was in college doing a NLP assignment that needed to be fast, my data structures were arrays of doubles because that was the easiest way to reason about and ensure performance.
Case in point: In C++ std::vector<double> and the standard algorithm header take care of most of that. If there is some specialized implementation of some algorithm for double values then you can still use that without copy pasting the implementation of everything else.
I purposely left out templates because I do not consider them to be the same as generics. Their is no "runtime" support for templates; templates are basically a glorified macro engine for creating new code based on type parameters. But yes, templates by definition specialize since they are creating new code every time they are instantiated.
At runtime any Java List is a list of Objects, you cannot distinguish an ArrayList<Integer> from an ArrayList<Double> both are a raw ArrayList. What runtime support do they have?
> I feel bad for the C++ linkers that have to sift through all the template bloat to dedupe them :(.
If you have a template used often enough you can declare it extern and force instanciation in a specific cpp file.
There actually is some basic runtime support for generics, just enough to avoid breaking the runtime. For instance, if I create a class MyList extends ArrayList<Double>, there actually is some runtime information that says MyList is an ArrayList<Double>, not just an ArrayList.
You're right though that saying templates are different from generics is based on opinion, not fact. There's no widely-accepted definition of both that would separate them as distinct from one another.
Generics just bake a little bit more into the runtime; there's already a lot of the type system there. And it increases interop potential. I don't understand your point about Scala - they could always choose to use type erasure instead of any runtime-provided generics. F# implemented generics with erasure on the 1.x CLR, and used the runtime when on CLR 2.
And this is the first time I've heard that the JVM is easier to target for non-Java languages. .NET has had multi-language support an official design goal, the JVM is just for Java. Even simple stuff like pointers, so you can do ref/out parameters. Or unverified instructions. The CLR is simply more all-around capable.
I am pretty sure the CLR always supported variance, it was just unused up until C# 4.0. AFAIK, there haven't been any changes to the runtime bytecode since CLR 2.0, when generics were added.
The performance benefits of specialization are pretty nifty. The Mono guys ported Android to C# and got quite the performance improvement on some code: https://blog.xamarin.com/android-in-c-sharp/ - so that's existing code that got a perf benefit "for free" due to not using type erasure.
Edit: A quick skim of the specialization proposal for Java[1] makes no mention of other languages. Just the general pain of integrating it in a compatible way.
Isn't that more a function of MS's hostility to other platforms, not technical reasons? If MS had been more open and friendly 15 years ago things might be different.
Here's a post from a JRuby developer saying he prefers a simpler VM.
In the comments section of the same post you'll see Martin Odersky, creator of Scala, saying he also prefers a simpler VM. I could probably find more examples if I dug further.
>I am pretty sure the CLR always supported variance, it was just unused up until C# 4.0
I can't speak about any of the programming language experts you've seen, but:
> To implement this feature, the C# language designers had to modify the underlying runtime itself and add special bytecode instructions for the feature
I think this argument is misconstrued. This is inherent to the way the CLR was designed. It is not meant to be something that is kept as-is in terms of the bytecode it accepts. It is constantly being improved upon. Most new language features to C#, F#, or other CLR languages mean an update to the CLR if they're related to a new feature in the abstract.
While there's an argument to be made on whether this better than a more generic runtime, the point is that requiring a change to the CLR is not a big deal; that's part of their plan.
It should be plain to see that it would be much easier to implement your programming language on top of the CLR or JVM if you didn't need to request new features from Oracle or Microsoft to be added to the runtime.
Which is why I've been very happy to see .Net Core take the path it has... it's been much more open to outside collaboration even if the bulk is done by MS employees.
Wasn't it too late? I thought the reason they went the type erasure path was because they didn't want to take the compat hit of changing the bytecode that much. Is that not accurate? Maybe if they had done generics originally it'd have been built in to the runtime. Isn't this potentially going to happen in Java10 or something?
So? Java doesn't have channels either (except in third party libs added much later), but they were in Go from the start. Same for e.g. plain structs, which Java will only now acquire.
When one language added a feature has no bearing to when another should add it.
FWIW Go is almost 8 years old now, and there is absolutely no plan to implement generics in anyway right now. I think they just can't without breaking backward and forward compatibility, they should just admit it instead of trying to avoid the question.
I'm a Go user, both at work and at home. My most-wanted feature is Generics, but I trust the Go core team when they say they want to ensure the feature is implemented correctly. The absolute last thing I want is for them to rush some half-assed implementation, which becomes semi-permanent because they can't fix it without breaking everyone's code.
Coding without generics turns out to have many advantages. Mostly code bases that need to evolve rapidly without getting locked in to abstractions that don't fit anymore. It certainly is a mindset shift, and may be better at scale, but it's also more tedious and sometimes you just want a freaking Foo<Bar>.
>Coding without generics turns out to have many advantages. Mostly code bases that need to evolve rapidly without getting locked in to abstractions that don't fit anymore.
How would Generics restrict you any more than the numerous variations on the same functions for different types (that you need today)?
Except if the alternative is using interface{} for everything, in which case, yes, Generics would be more restrictive.
Generics would make go code safer, less reliant on complicated tricks to circumvent language limitations and the need for reflection and ultimately make programs less brittle since it would reduce the amount of code needed for a task in general. The only valid argument against them is compilation speed but it hasn't been tried in real.
I'm not a Go programmer as I like my languages baroque, overcomplicated and with ridiculously overpowered generic systems [1], but there value in simplicity and maybe a macro system would be a better fit for go than fully featured generics.
If you are a C++ programmer then you know that the latest features that were added aim at making C++ programming simpler and easier. Adding something to a language to fix its flaws is not necessarily adding some complexity.
That they already have in the form of Go generate, although it brings me memories from MS-DOS Borland C++ before they implemented templates and used preprocessor macros.
A quick look shows that 'go generate' simply allows filtering a file through an external tool. This is a powerful feature but has great setup overhead. Something slightly more integrated with the language (as a distinct compiler pass but still part of the language), with the capability of defining macros inline (and being exportable), and AST based instead of string based would be better.
Users always like simplicity in everything. They want a big red button that does what they need. Which is why asking them about design is generally useless. They would choose simplicity every time.
There are, of course, some users, that can make substantial comments about design, but they cannot be represented in a survey.
so 84% don't want generics, right? :) I switched to Go from Python several years ago, I admit that I missed generics for the first several weeks but then just moved forward. Nowadays I don't really think I actually need them anymore.
Generics are a mechanism by which a statically-typed language can approach the generality of abstraction possible in a dynamically typed language while preserving static type safety, so even though Python doesn't have generics, one absolutely would have good cause to miss them going from Python to Go.
Because everyone of us that cares about generics uses languages that support them, and most likely a Go++ effort would be in vain, after all there are already a few Go preprocessors, which don't seem to be widely adopted.
Take a look at CLOS. It's for a dynamically-typed language, but the same principles could be used to provide for multi-methods in a statically-type language like Go.
With what money and whom to maintain the fork? The survey even shows that most go users never or rarely contribute the any open source go project (25% + 34% respectively). You'd need a solid corporate backing.
I have repeatedly thought about building a variant of Go that compiles down to Go and then gets compiled by the upstream compiler, mostly to add generics and autogenerate all these
if err != nil {
return nil, err
}
blocks. But it would most definitely be a pain in the ass to integrate this with the toolchain.
What I think is a bit remarkable is that it's not so much generics which are lacking, but syntactic abstraction. Folks focus on generics, but really the one thing Go is missing is the ability to extend the language as a programmer.
and:
>What changes would improve Go most? (first: Generics)
Number one issue mentioned.
So much for all those insisting that Go users have no issues with the missing Generics and don't miss/need them in practice.