In what way is Go code composable?
In what way is Go code composable?
There is countless proof about Go's composability through functions and interface, but I keep coming back to that one document.
I can't tell if you're serious or sarcastic yourself. The article proposes this snippet, presumably copy/pasted for every option:
// Verbosity sets Foo's verbosity level to v.
func Verbosity(v int) option {
return func(f *Foo) option {
previous := f.verbosity
f.verbosity = v
return Verbosity(previous)
}
}
If that's what Go composability looks like then...uh... No thanks.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.
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.
This looks indeed more sensible.
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.
Edited for clarity.
AKA macros.
I wish they were at least intellectually honest and just call them "macros" and start nice supporting hygienic macros in the language.
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 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.
Technically, it's composability, just not the best kind.
compose :: (b -> c) -> (a -> b) -> a -> c
? compose :: (b -> c) -> (a -> b) -> a -> c
Other than that, I agree!You think right, that'll teach me (once again) not to typecheck my comments. Thanks, and fixed.
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.
[0] https://wiki.mpi-sws.org/star/paramore?action=AttachFile&do=...
[1] These kind of variables, not "mutable cell" style variables: https://existentialtype.wordpress.com/2012/02/01/words-matte...
That's the key - generics are one way to accomplish things, but they're not the only way.
I don't know that sarcastic is the correct word, but I certainly wasn't completely serious. Maybe "playfully antagonistic".
I suppose the problem I have here is, if I was designing a language, and composability was a stated goal, then I wouldn't have come up with Go.
> a brilliant article from Rob Pike about using and composing functions to create flexible APIs
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.