6 ms·
Go's support for first-class functions definitely sparked my interest in the language. But one thing that Javascript, Python and Ruby programmers would expect o
by jeremyjh 14y ago
Go's support for first-class functions definitely sparked my interest in the language. But one thing that Javascript, Python and Ruby programmers would expect of higher order function support is the ability to use the general collection methods such as map, reduce, filter (or inject, collect, fold - whatever they are called in your language). So far as I can tell, generalized functions of these types are not really possible. You could write some that were typed interface{} and then did explicit casts in the closures you passed, but that would not be nearly as elegant as what other languages provide. I believe in a static type system without generics this is not really possible, and this is really (to me) the most painful cost from the lack of generic support.
- koenigdavidmj 14y agoGo is an opinionated language. Go people would just tell you to write the loop; that's what it's there for.
- jlgreco 14y agoTo be clear, generics seems to be one thing that Go isn't actually opinionated on. As I understand it, they are open to including generics in future versions of the language, but only if the implementation is elegant.
- _ak 14y agoWell, as far as I understand, that implementation would need to overcome the Generic Dilemma as described by Russ Cox: http://research.swtch.com/generic http://research.swtch.com/generic So far, the Go team decided to go for approach #1.
- maaku 14y agoOption #3 is unfairly presented. There is no reason a language couldn't implicitly box every type, but automatically create efficient specializations for (some of) the types actually used. You can have your cake and eat it too. Of course, it's not easy to write such static optimizations. But it is, in principle, possible, and the sort of thing you only need write once.
- dualogy 14y ago> But it is, in principle, possible, and the sort of thing you only need write once. Well Go is open source so "someone" would just have to write, test and submit a CL / patch... ;-)
- masklinn 14y ago> So far, the Go team decided to go for approach #1. Except where they personally really wanted parametric polymorphic collections so they baked them as special case into the runtime, of course (map, chan, …)
- codeflo 14y agoIn a sense, they went for #3 (auto-boxing) without the syntactic sugar. Casting int to interface {} has a similar overhead to boxing in Java.
- jerf 14y agoThe loop was a trivial example. Less trivial examples are not possible either. For instance, I'd like to write a function that takes in a channel and returns a (directional) channel that has been turned asynchronous, by running a goroutine that manages a queue and doing some other things above and beyond what a merely buffered channel does (emulating Erlang's way of managing async messages with backoff timeouts), but I can't do that correctly and safely. I haven't completely written this function in fact but it's certainly at least 20 lines. I'm making a list of the things that I tried to do in the language, then couldn't; I'm up to four right now, each of which are not trivial examples, but are instances of code that I must repeat because I can not refactor it. This is not a plus. (My list is at home and I'm not, and I'm sort of saving it for a blog post anyhow, but this is representative; not killer, but the issues compound as the program gets large.) Mind you, I know the language is young, etc etc, and I support the idea of doing it right rather than doing it sooner, nor am I attached to "generics" as the only possible solution. However, there is a real problem here for refactorability, and I would not want the Go community to build too much of a metaphorical callous around this issue and get to the point where they just reflexively respond with annoyance. There is a problem here.
- zemo 14y ago>For instance, I'd like to write a function that takes in a channel and returns a channel that has been turned asynchronous, by running a goroutine that manages a queue and doing some other things above and beyond what a merely buffered channel does, but I can't do that correctly and safely. see here: http://gowithconfidence.tumblr.com/post/31426832143/stacked-channels http://gowithconfidence.tumblr.com/post/31426832143/stacked-... the trick is to make a channel of buffer size 1, then when adding to it, simultaneously try to read and write from that channel using a select statement. Only one will ever be ready. You could also do something similar really horribly with interface{} but that's almost always a code smell: https://gist.github.com/jordanorelli/3668150 https://gist.github.com/jordanorelli/3668150 I probably wouldn't do that in production code. Either way, generics in Go remains an open question.
- jerf 14y agoThat's simpler that what Erlang does (you need some code for the time backoff, and while I think Go can do it it is inherently the sort of code that ends up a complicated mess of cases that, if I could lock it behind a function, would be very well abstracted away for the user), but it is the basic idea. The problem is, you have to copy & paste those lines of code for every type you want to do that with; you can't vary that code on "Thing". In this case the fact I'd have to use interface{} isn't a code smell, it's a language smell. Looking in two different "directions" from Go, that's trivial to implement as a single Thing-agnostic function in the weaker-type system Python (gevent queues have similar enough semantics for this to be a fair comparison), and it's trivial in stronger-type system Haskell (also similar enough semantics with TChan to be fair). Also I note you did not return the chan for the user to do with as they like, abstracting out the mechanics from the task, but have a hard-coded "join" function in there. You can easily take in a function to "do something", but you won't be able to do anything with the resulting value, not even return it, unless via inteface{}. (Which makes me cringe every time I type it.) Again, to be clear, I'm not trying to dump on Go here; there's a lot of things to like about it, especially its infrastructure. But this is by far my biggest pain point with it personally. YMMV.
- dualogy 14y agoI feel fairly productive in Go, having initially decided "OK well let's just see how far I'll get without generics before giving up". Turns out the instances where I think I need generics are much rarer than I initially expected. Also, sometimes we just need a bit of template programming rather than full-blown generics. Templates let you express whatever you'd use generics for but also much more. So once you realize you won't need generics if you could just get some template programming support, the problem becomes trivial: there isn't any out-of-box support for templating but it's trivially easy to write the bit of string processing required for a pretty general reusable flexible fast poor-man's templating preprocessor, and even easier to hook that process in your build routine. Some people use "gotgo", I rolled my own minimalist template preprocessor: https://github.com/metaleap/go-buildrun https://github.com/metaleap/go-buildrun Now you'll see I have written some sizable amounts of Go code at https://github.com/metaleap/ https://github.com/metaleap/ and https://github.com/go3d/ https://github.com/go3d/ yet I have only had to use that "templator" in a very few rare instances. But then, I didn't come to Go with a functional-programming mind-set, which automatically means I somehow don't actually need to map-reduce-filter-collect-fold-etc. that often... ;-)