So maybe 7-8 years ago, I got into a bunch of arguments with people on Hacker News because I said that Go's type system was ineffective without generics. At that time, Gophers leaped to defend it, going so far as to say that it's better because it's simple. Oh and it was really important to Gophers that Go compiles in a single pass (which I argued is only relevant for truly enormous codebases like Google's).<p>A few years later we had go generate. Which I argued was a glorified C macro system to get around the lack of generics. Oh and everybody forgot about the single-pass thing, I guess.<p>Now in this thread there are a bunch of people commenting that they can't wait to combine all their <i>copy pasted code</i> using a generics which are implemented via a tree traversal that actually kind of complicated the idea of what a pass even is (I think memoization could make this linear, but at the cost of memory?).<p>This is a language that seemingly insists on breaking the wheel. Notice I didn't say <i>reinventing</i>, because in over a decade they haven't actually gotten a working version of the wheel out yet. I mean seriously, Go was released in 2009: if I'm not mistaken, C# had a pretty good generic system already, and there were a bunch of other workable options to copy from less-mainstream languages. Not that copying has helped: they copied coroutines from other languages, but failed to present a coherent memory sharing model (just copy Erlang FFS), which severely limits the usefulness of coroutines.<p>Every few years someone gets me to try Go out again and I discover, yet again, that it's still struggling with problems that were solved before it existed. It's a shame that this language has stolen so much mind share from more deserving languages.
This approach (sharing code for various generic instances and passing in a blob of type information) is used for generics in some other languages/runtime environments - for example .NET will in many cases do code sharing like this where it will generate a reusable generic function that operates over many types and then pass it type information so it can operate properly instead of having to generate 50 different versions of a function like a C++ compiler does. This obviously can have some performance implications, but it makes sense to do it (especially in Go's case where what came before it was tons of virtual calls anyway).<p>In .NET on Windows you can sometimes observe this because generic types in your stack traces (in the debugger) will be replaced with 'System.__Canon' [<a href="https://twitter.com/matthewwarren/status/1161249300401311745" rel="nofollow">https://twitter.com/matthewwarren/status/1161249300401311745</a>] instead of the actual type - this indicates that the type was completely erased, so the current function could be running for any number of types and the type can't be identified based on the current instruction pointer.<p>The shared code + blob approach becomes more necessary in an AOT compiled environment like Go (and you'll see it used more in AOT compiled modes for .NET) since you can no longer rely on being able to on-demand JIT some optimized code when a new type shows up.
I thought this was something like traits, but it goes way beyond that.<p>Sub-dictionaries are described here: <a href="https://github.com/golang/proposal/blob/master/design/generics-implementation-gcshape.md#subdictionaries" rel="nofollow">https://github.com/golang/proposal/blob/master/design/generi...</a><p>It looks like the compiler needs to walk down the entire call tree from the top-level generic and then compute new dictionaries for each called function. Since the compiler may not know the entire call tree, it may have to build nested dictionaries.<p>Wacky stuff!
Does Go support separate compilation? The approach sounds like a change in the implementation of a generic function (changing the gcshapes) could cause client code to break unless it is recompiled as well. What am I missing?
By the way, are there any projects underway to produce a library of the most common basic template functions? I'm very much looking forward to unifying some of my most copy-pasted functions when 1.18 is out, but I would like to unify on a central library right away.
I made a crude proposal for generics in 2011[1], in which I proposed a pair of concepts ("storage class" and "type class") that are somewhat similar to the concept of this "gcshape".<p>I proposed it as a compromise between full monomorphisation and runtime code generation.<p>[1] <a href="http://oneofmanyworlds.blogspot.com/2011/11/draft-2-of-proposal-for-generics-in-go.html" rel="nofollow">http://oneofmanyworlds.blogspot.com/2011/11/draft-2-of-propo...</a>
Something I haven't been able to pull up yet: what does the addition of generics do the `reflect` package? I assume it needs to be extended to deal with reflection through generics?
Disclaimer: I only very occasionally touch Go code.<p>Hasn't this been a very long time coming? Iirc there has been much dispute over this in the community. Can anyone weigh in on what the other options were?
Programming languages are lacking because they are too stuck in the "implementation plane" while trying to deal with lots of "system design" problems. Generics, traits, interfaces, union types and others are fundamentally targeted at giving developers more expressive power to describe the <i>systems</i> we are designing. We know there are many parts we could swap around, using different implementations, connecting some pieces here and there... and the system should make sense and work. We can see that it must work! But these features are trying to resolve problems from a very closely-connected but still different domain, and that's why we see so much friction when trying to use them. We try to encode system-level patterns in the implementation, and there's gonna be friction. We can see that these features give us power, and that's why we like them, but we also see the problems they cause, and that's when we get cold feet and say "yeah... maybe it's not such a great idea".<p>I'm actually really happy to get generics in golang, and I'm happy with the team giving it as much thought as they need, but we are only gonna get so far within the current paradigm of trying to model the universe from a few text files. Generics are nice, but we shall do better in the future!