Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You probably were sarcastic, but in case you are not there's a brilliant article from Rob Pike about using and composing functions to create flexible APIs: http://commandcenter.blogspot.co.uk/2014/01/self-referential...

There is countless proof about Go's composability through functions and interface, but I keep coming back to that one document.



There is countless proof about Go's composability through functions and interface

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.


Unfortunately that is what I've experienced.

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.


> presumably copy/pasted for every option

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.

[0] https://golang.org/doc/effective_go.html#embedding


> 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.

Edited for clarity.


go generate

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.


> "go generate"

AKA macros.

I wish they were at least intellectually honest and just call them "macros" and start nice supporting hygienic macros in the language.


Yes, I do see macros as a possible alternative to generics, but not sure if it would ever happen.


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 know, but they surely would be a better solution than "go generate", as I doubt Go will ever have generics.


> You probably were sarcastic

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'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.

[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...


> generics as a powerful tool for using types to erase inessential details.

That's the key - generics are one way to accomplish things, but they're not the only way.


I wouldn't argue otherwise. Instead, I argued that types are the way and that without generics your type language is crippled.


While that's true, I don't think that's enough to make things "orthogonal," at least as I would use the word.


Anything to do with higher-order functions, least of all

    compose :: (b -> c) -> (a -> b) -> a -> c

?


I think there's a typo in your signature. It should be:

    compose :: (b -> c) -> (a -> b) -> a -> c
Other than that, I agree!


> I think there's a typo in your signature.

You think right, that'll teach me (once again) not to typecheck my comments. Thanks, and fixed.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: