Live data from Hacker News

How generics are implemented in Go 1.18

github.com

1–10 of 228 posts

Re: How generics are implemented in Go 1.18

#3

Disclaimer: I only very occasionally touch Go code. 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?

In a nutshell, torn between the extreme ends of high compile-time cost and large code size of a C++ flavored implementation and the high runtime cost and dangers of boxing/unboxing (Java), Golang opted for "none of the above" and tried to limit complexity drivers / powerfulness / expressiveness / usefulness instead, keeping the impact on both compile time and run time low.

About how well that decision will turn out in practice, we'll start to know soon.

Re: How generics are implemented in Go 1.18

#5
post #3

Disclaimer: I only very occasionally touch Go code. 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?

In a nutshell, torn between the extreme ends of high compile-time cost and large code size of a C++ flavored implementation and the high runtime cost and dangers of boxing/unboxing (Java), Golang opted for "none of the above" and tried to limit complexity drivers / powerfulness / expressiveness / usefulness instead, keeping the impact on both compile time and run time low. About how well that decision will turn out i…

The compile time issues for C++ are entirely because of the creaky header mechanism that doesn't permit efficient parsing. Generics have worked fine in Ada since its inception. Any modern language can adopt the same process of tracking a formal abstract interface that is parsed once and expanded into concrete code as necessary.

Re: How generics are implemented in Go 1.18

#6
post #3

Disclaimer: I only very occasionally touch Go code. 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?

In a nutshell, torn between the extreme ends of high compile-time cost and large code size of a C++ flavored implementation and the high runtime cost and dangers of boxing/unboxing (Java), Golang opted for "none of the above" and tried to limit complexity drivers / powerfulness / expressiveness / usefulness instead, keeping the impact on both compile time and run time low. About how well that decision will turn out i…

This summary concisely captures the key thing about generics that has kept them out of Go for so long: they weren't created in a vacuum. Go's developers had the flaws in C++ and Java's approaches to draw on in their attempt to find a better approach. Many people calling for generics to have been implemented sooner were fine with those flaws in the other languages, but the Go team wanted to do better than that.

Re: How generics are implemented in Go 1.18

#7
post #3

Earlier quoted context omitted.

In a nutshell, torn between the extreme ends of high compile-time cost and large code size of a C++ flavored implementation and the high runtime cost and dangers of boxing/unboxing (Java), Golang opted for "none of the above" and tried to limit complexity drivers / powerfulness / expressiveness / usefulness instead, keeping the impact on both compile time and run time low. About how well that decision will turn out i…

The compile time issues for C++ are entirely because of the creaky header mechanism that doesn't permit efficient parsing. Generics have worked fine in Ada since its inception. Any modern language can adopt the same process of tracking a formal abstract interface that is parsed once and expanded into concrete code as necessary.

Rust also has (relatively) bad compile times due to generics. Not as bad as C++, but you can notice it's significantly worse than Go.

Re: How generics are implemented in Go 1.18

#8

Disclaimer: I only very occasionally touch Go code. 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?

> Hasn't this been a very long time coming?

That's a feature not a bug of golang. Major language changes like this by design are supposed to take a lot of time and thought to land.

Re: How generics are implemented in Go 1.18

#9
post #3

Earlier quoted context omitted.

In a nutshell, torn between the extreme ends of high compile-time cost and large code size of a C++ flavored implementation and the high runtime cost and dangers of boxing/unboxing (Java), Golang opted for "none of the above" and tried to limit complexity drivers / powerfulness / expressiveness / usefulness instead, keeping the impact on both compile time and run time low. About how well that decision will turn out i…

The compile time issues for C++ are entirely because of the creaky header mechanism that doesn't permit efficient parsing. Generics have worked fine in Ada since its inception. Any modern language can adopt the same process of tracking a formal abstract interface that is parsed once and expanded into concrete code as necessary.

Isn't that solved with c++ modules [1]?

[1] https://en.cppreference.com/w/cpp/language/modules

Re: How generics are implemented in Go 1.18

#10
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).

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' [https://twitter.com/matthewwarren/status/1161249300401311745] 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.

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.

Post reply on HN