I direct your attention to sort: https://code.google.com/p/go/source/browse/src/sort/sort.go#...
This defines sort on []int and []float64.
If you want to sort a []int32, you have to manually define Int32Slice and copy paste lines 232-237. Copy paste the same lines again for any other slice of built-ins you wish to sort, e.g. uint32, uint64, int64, float32, etc.
They removed the entire set of vector containers now, but for the canonical way to implement IntVector, see https://code.google.com/p/go/source/browse/src/pkg/container... -- and compare that to stringvector.go in the same directory. It's almost entirely copy/paste with string substitution to replace int with string.
To be fair, there is "go generate" now that can generate functions that would otherwise require tedious copy/pasting. If you are familiar with the preprocessor in C++ expanding template definitions, this tool can provide similar functionality but is more generalized.
I think embedding [0] is the real big composability win in Go.
> To be fair, there is "go generate" now that can generate functions that would otherwise require tedious copy/pasting. If you are familiar with the preprocessor in C++ expanding template definitions, this tool can provide similar functionality but is more generalized.
Comparing to C++ templates is a rather generous comparison. C++ templates provide a rich type system that allows type checking and prevents errors, and integrates with the rest of C++'s type system.
Go generate is a lot closer to C/C++ macros, the only improvement being that the generation is done in a turing-complete language instead of by text substitution.
In C-like languages this problem is typically solved with generics, but the go team has decided to ignore 20+ years of language development and try the same thing that didn't work so many times that it motivated the creation of generics.
"But," you say, "Generics slow down compilation!" To which I respond that the first step to compiling code is creating code that does something worth compiling.
I agree, it is a generous comparison, which is why I only compared "go generate" to expanding template definitions (albeit, in hindsight, even that is generous). Macro expansion is definitely a better comparison. In no way did I mean to insinuate "go generate" is on par or better than C++ templating language features.
Ugh. Sounds like Go is turning into a language that I'll want to avoid in the future. There is no excuse for code generators. Friends don't let friends generate code.
I think embedding [0] is the real big composability win in Go.
Macros are not an alternative to generics, they're an alternative to C++'s template specialisation.
Most languages with generics don't provide for user-controlled code generation via generics (if they do generics-based codegen at all, IIRC GHC uses erased generics for instance). They tend to use macro systems or preprocessors instead.
> I'm sorry, but that API is not something to be proud of.
I agree: not only does using it require a lot of boilerplate, but Option takes several arguments and returns a value that only restores the previous value of the last option! That sounds very error prone, I'm sure someone will think the value return by Option undoes all of the option setting.
The absence of generics makes it so that Go's composability is through copy/pasting your code while changing the types. There's even a tool to do the copy/pasting for you.
Technically, it's composability, just not the best kind.
Generics and composability are orthogonal concepts, to me. I'm not really sure what sort of composability you're talking about. Can you provide an example for me to rip apart?
"Modularity" is nearly defined by the ability to forget certain details of an interface so as to leave freedom to change the underlying details while maintaining the behavior at the interface (Reynold's Abstraction Principle). In almost the same breath one would declare that types are a primary mechanism to achieve this. [0]
Type structure is a syntactic discipline for
enforcing levels of abstraction.
Modularity is more or less required in order to take a stab at defining or talking about composability, but is "good" modularity necessary (or sufficient) for achieving composability?
I don't have a bulletproof argument here, but essentially I'd like to say that greater modularity allows you to forget inessential details and subsequently increase the surface area with which you can compose your modules.
So finally, we come to generics as a powerful tool for using types to erase inessential details. Generics essentially operate via providing a new logical structure to types, the ability to introduce and operate sensibly over type variables. So I would argue that a type language without generics is much like a programming language without variables [1]. You are perhaps not completely crippled, but pretty close.
So, my argument is something like "without generics your type language is crippled so as to make control of modularity very weak which will prevent you (syntactically) from ensuring composability".
The "(syntactically)" bit is important since, as we all know, you can achieve modularity and composability "by hand" through code generation, copy and pasting, creating commenting policies... but then we've stumbled back into the "static types versus dynamic types" argument, so let's not rehash it.
There is countless proof about Go's composability through functions and interface, but I keep coming back to that one document.