Why Go’s design is a disservice to intelligent programmers

My first experience of generics was actually with Ada. AFAIK the language spec. didn't explicitly state the implementation, but it was well known that the standard implementation was basically tables of function pointers. One way you could see this is in the limitations in what you could do with an Ada generic in comparison to a C++ template - there's even C++ template libraries that generate LALR(1) parser state models at compile time because C++ templates are a Turing-complete language in themselves, whereas last time I checked Ada, Java and C# generics (probably quite deliberately) were not.

C++ templates are the canonical way to do C++ templates. Actually, AFAIK C++ doesn't specify the implementation of templates in the standard, and neither did Ada back in 1983. Nevertheless it was widely understood that the canonical implementation was essentially vtables in Ada, just as it's widely understood that the implementation is essentially replication and substitution in C++.

Of course the function pointer tables aren't the whole story. Java, Haskell, Ada etc generics may be "canonically" based on vtables but inlining decisions often mean generic code gets replicated-with-substitutions anyway, maybe sometimes even to the point where the vtable won't be linked because nothing actually uses it (depending on details of the linkers and compilation units of course).

On the other hand C++ in principle could decide to optimize bloat away by choosing to use vtable-based template implementations, though I doubt any compiler tries to implement that. What actually happens is design patterns that mix templates with classes so the vtables happen because of the inheritance hierarchies - and then potentially get optimized away via inlining decisions.

The point being that there's a tendency that one way or another there's a tendency to converge on roughly the same optimal point, even in languages that seem to start from quite different places. The default implementation of an abstraction can be optimized into something quite different - or if that leap's sufficiently important yet beyond real-world compilers there's probably a common design pattern to get a similar effect.

Both the replication-and-substitution and tables-of-function-pointers methods are textbook implementations of generics. The function-pointers method has some limitations in what it can do last time I checked these limitations were quite visible in the capabilities provided by the generics in Java and C#.

Of course those limitations could be overcome by moving more work from compile-time to run-time and adding more to the tables than function pointers - e.g. sizes and alignments for parameter types so that compound sizes could have layouts designed at run-time.

Technically speaking, you could argue Haskell doesn't have generics - it has pervasive parametric polymorphism. Of course one rather intuitive way to explain this is that everything's generic by default - it's only a lack of unbound type variables that makes anything non-generic. The Haskell typesystem provides another Turing-compile compile-time sublanguage but that involves more than just parametric polymorphism - there's effectively Prolog (or predicate calculus) in type signatures that use constraints and the typeclasses those constraints reference. If you want you can literally strip out all the type signatures, perform a fairly simple translation to Prolog, and run to figure out what the Haskell program figures out at compile-time.

Of course there's also Template Haskell - not really like C++ templates but, unlike the parametric-polymorphism "generics" the standard implementation is code replication and substitution. Actually it goes beyond that to full programmatic manipulation of ASTs via ordinary Haskell code with a few extra template-Haskell-specific types and operations. Haskell devs. tend to look at TH with suspicion - it's generally considered a brute-force workaround.

/r/programming Thread Parent Link - nomad.so