Currying in Go

1,246 views
Skip to first unread message

Zauberkraut

unread,
Jun 16, 2016, 6:00:43 PM6/16/16
to golang-nuts
Hello,

Go enables the evaluation of functions using currying over function literals. Every example I've found of this is rather shallow; a "deeper" example I wrote implementing (x => (y => (z => x^2 + y^2 + z^2))) follows:

func f(x int) func(int) func(int) int {
return func(y int) func(int) int {
return func(z int) int {
return x*x + y*y + z*z
}
}
}

Go's limited type inference makes the explicit, cascading function types necessary; this seems like an eyesore and maintenance concern. While I don't really mind it, it does cause me to hear code review sirens going off in the distance. Generally speaking, would an extended usage of this paradigm be considered unidiomatic in Go? Obviously, the above example is contrived and not the sort of use case in question. Thanks!

adon...@google.com

unread,
Jun 16, 2016, 9:42:29 PM6/16/16
to golang-nuts
On Thursday, 16 June 2016 18:00:43 UTC-4, Zauberkraut wrote:
would an extended usage of this paradigm be considered unidiomatic in Go?

Short answer: yes.  Excessive use of function values as arguments and results of other functions can make the flow of control hard to follow.

Henrik Johansson

unread,
Jun 17, 2016, 1:59:56 AM6/17/16
to adon...@google.com, golang-nuts

Note the _excessive_ caveat. Used with some restraint I think it is a very powerful construct.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Patrick Logan

unread,
Jun 17, 2016, 2:37:12 AM6/17/16
to golang-nuts
Go allows functions to have multiple arguments. The upside is currying is much less necessary in languages like Go. The downside is combining one-argument functions to make new one-argument functions is syntactically more cumbersome.

evan....@go2mobi.com

unread,
Jun 17, 2016, 2:57:37 AM6/17/16
to golang-nuts, adon...@google.com
I played around tonight trying to come up with a better way, but instead I came up with 2 decidedly worse ways (particularly considering readability/maintenance is the primary concern of the question). I think they're novel enough to share! 


1) Original
2) Defining types with a consistent naming scheme - less typing, more obscurity!
3) Make a function that auto-currys - Like a curry-compiler... except requires you to type a whole auto-curry function along with some magic types to compile every possible combination of params and outputs! MAGIC!

I think if you lift the burden of pure currying (one argument always) and use closures with multiple arguments unless you specifically need to be able to pass around partial functions with one argument you will free yourself from producing cumbersome code like this.

That said, if each single argument partial function has a purpose as understood by its users, then you should be able to come up with a type name for each nested function and use that in your definition to clarify the intent of each "layer". It's pretty standard in Go to give type names to functions make explicit what you need. I parody this in my second example, but with proper names I think it could be quite readable code--it's really hard to say without seeing your specific use-case.

If you can't come up with adequate names for each layer, you may want to ask yourself: If each of your partial functions doesn't have a purpose on its own, then why are you doing it?

Just my 2 cents.

parais...@gmail.com

unread,
Jun 17, 2016, 9:31:08 AM6/17/16
to golang-nuts
What is the point of doing that in a language that doesn't support auto currying ? There is none. You are not currying anything by the way, you just wrote 3 closures. 

Evan Digby

unread,
Jun 17, 2016, 10:34:04 AM6/17/16
to golang-nuts
Currying is translating the evaluation of a function with multiple arguments into evaluating a sequence of functions with one argument. Not sure how this doesn't qualify, even if a closure was used to accomplish this.

As for the value, at the very least there is the same value as using closures in general. The rest (why converting to single argument functions for pure Currying was necessary) would have to be use-case specific. The example given doesn't speak to why it's valuable.

I would be curious to understand the value by exploring more real-world use-cases myself!

Ian Lance Taylor

unread,
Jun 17, 2016, 2:59:37 PM6/17/16
to golang-nuts
Note that Go's reflect package is powerful enough to implement
currying directly, though you do have to convert back to the expected
type in order to call the function.

https://play.golang.org/p/2ukRfHGnlT

Ian

Tyler Compton

unread,
Jun 17, 2016, 11:32:18 PM6/17/16
to golang-nuts
I would also like to hear from somebody about a real world use-case. I don't pretend to be proficient in this realm of functional programming, but I would be very surprised if this is valuable in a language like Go that can and does hold state. That said, this is very cool and I would love to be proven wrong.

Christoph Berger

unread,
Jun 19, 2016, 6:45:18 AM6/19/16
to golang-nuts
> this seems like an eyesore and maintenance concern. (...) it does cause me to hear code review sirens going off in the distance. 

Then why would you want to use currying in Go at all? What's the point of being able to write f(1)(2)(3) instead of f(1,2,3) in a non-functional language? Especially if the price you pay is code that is hard to read and difficult to maintain. 

As a Go proverb says, "Clear is better than clever".

Jesper Louis Andersen

unread,
Jun 19, 2016, 11:15:13 AM6/19/16
to Tyler Compton, golang-nuts

On Sat, Jun 18, 2016 at 5:32 AM, Tyler Compton <xav...@gmail.com> wrote:
I don't pretend to be proficient in this realm of functional programming, but I would be very surprised if this is valuable in a language like Go that can and does hold state.

In functional languages, it is often used as a way to "configure" a function. I.e., the function is defined as 

let f conf x = ...

where 'f' is the function name, 'conf' a parameter of configuration and 'x' the "real" argument to the function. Now, partial application such a (f c) for some configuration 'c', yields another function which has been configured, or specialized, to the particular use case.

Usually it allows you to define a generic function and then specialize it in some context by "plugging in" a configuration. The configuration is often a constant value which means the compiler will typically constant-propagate that value into the closure. Many functional compilers understand that closures with static data can be eliminated at compile time by replacing them with a normal function. In other words, you have a generic variant of your function, but when run it is specialized and instantiated into an optimized variant.

You *can* get the same with a function such as 'f(conf, x)' but in this case, the compiler usually has some more work to do before it can apply the above transformation.



--
J.

Tyler Compton

unread,
Jun 20, 2016, 5:02:41 PM6/20/16
to golang-nuts, xav...@gmail.com
Thank you, Jesper, that's very interesting.
Reply all
Reply to author
Forward
0 new messages