First off, I always want to say ‘Currying’ because, it seems to me, that the whole point is to take a function an apply the Curry technique to it, thereby ‘Currying’ the function. It just feels like it needs an ‘ing’. But I suppose that is neither here nor there.
The real sticking point for me has been finding examples of real-world applications for currying, something to which I might be able to relate, but these seem to be few and far between. StackOverflow let me down in this department, but Reddit seems to have gotten a bit closer(?) Almost every other example I ran across when reading about currying was some form of addition. Exciting, right? “Hey! Look! We built a function that you can pass a number to, and now every other time you call that function and pass it a number you will get back the sum of the very first and very latest number! Woot!”
Then, as an extra rub, there is something called ‘Partial Application’ which feels pretty similar but is technically different. So, let’s start off by looking at some definitions (Courtesy of Eric Elliott. Is it really courtesy when the person has no idea that you’re straight lifting their ish? Maybe there’s an informal definition. Note to self, google later):
A function that takes a function with multiple parameters as input and returns a function with exactly one parameter.
... a function that takes a function with multiple parameters and returns a function with fewer parameters. Partial application fixes (partially applies the function to) one or more arguments inside the returned function, and the returned function takes the remaining parameters as arguments in order to complete the function application.
So, the difference is that (again, thanks to Eric Elliott):
A curried function always returns another function with an arity of 1 until all of the arguments have been applied.
A partial application may or may not have a predictable return type.
So let’s do this
So the idea is that we have a function that takes
n arguments, but we don’t have all
n arguments to hand at the moment.
… Fuck it, let’s add, ‘cause I’m not that creative and I can’t think of a better simple example at the moment.
1 2 3
We have an
add function that expects three arguments, but let’s say we only have one at the moment. We could save it to a variable, then save the second, then, when we have the third, finally call the function and pass along the three required arguments. Or, we can encapsulate the the data along with the logic into a deliciously curried dish.
1 2 3 4 5 6 7 8 9 10 11
We’ve created a closure. We can shove the variables
c into the closure, forget about them, and not have to worry about about mutating them somehow from outside out curried function.
To get the result out we just pass in our arguments one by one.
So we have a function that accepts one argument, aka a function with an arity of 1, so we’re following the definition of curry. We then feed in the first argument and get back another function with an arity of 1, pass in the second argument, get back a function with arity = 1, then pass in the final argument and getting back the expected return value.
1 2 3 4 5 6 7 8 9 10
Or, more concisely
Now that we can curry a specific function, let’s figure out how to curry any function that takes multiple arguments.
We first define a function that takes another function as an argument, the function we want to curry and pass arguments to singly.
We know that we need to do something until all the required arguments to execute our function, say our
add function are present. First thing is first, how many arguments are we waiting for?
1 2 3
We can make our
curry function figure out how many arguments the function it is passed is expecting and change the behavior based on whether we have collected enough arguments to run the function.
1 2 3 4 5 6 7 8 9
And look! We get to play with ES6’s sweet little spread operator. Here, if
curried is passed
n number of arguments the spread operator collects them up into a single array, just in case we cheat and pass multiple arguments instead of one at a time as we should when currying (arity of 1 and all), then we check the length of the resulting array. If we have the requisite number of arguments to run our function, we do so.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Spread Operator Syntax
For function calls:
For array literals:
1 2 3 4 5
Back to the show!
OK, so we know what to do if we’ve managed to collect all our arguments, but what if we don’t have them all yet? We want to return a function with an arity of 1 that takes it’s only argument, checks if it has enough arguments, executes the original function if it does, returns a function with an arity of 1 if it doesn’t… Sounds recursive to me!
1 2 3 4 5 6 7 8 9 10 11 12 13 14
From here we recursively call
curried and pass it the new argument as well as all the arguments already collected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
And cleaned up just a bit…
1 2 3 4 5 6 7 8 9 10 11
There! We created the ability to curry any function we like!
But just using curry to pass in arguments sequentially as they are available is boring. Maybe applying curry for reusability might make a more interesting or compelling argument. M. David Green offers an example where multiple outputs require some shared data, but other pieces of data are different. Here we have a function that returns a greeting:
1 2 3 4 5 6 7 8 9
Order matters here. You place the most common data as the first arguments, something like:
then pass in the unique arguments further down the line to improve reusability. The thing that is most likely to change from case to case should be last. Here it is the name.
1 2 3 4
greetAwkwardly forms a closure around the greeting, separator, and emphasis, and returns a function that can be reused for multiple names.
1 2 3 4 5 6 7
I don’t know if I’ll ever actually use curry. I haven’t run into it myself and it adds a bit of indirection. This exercise has, however, been useful in reinforcing the power of closures and, unexpectedly, made me take a closer look at the
One issue with curry that has been nagging at me, mostly because of all the addition examples, is that they frame the problem as knowing that you need a certain number of arguments, but not having them immediately available. Curry lets us form a closure around the values and pass in the remaining arguments as they come along. But what if the order of the arguments matter, like with the greeting example above?
1 2 3 4 5 6
What if you don’t know in what order the arguments will arrive? It wouldn’t be very useful to have something like:
1 2 3 4 5 6
Maybe you solve it with option parameters, which might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Maybe this is a misuse of curry altogether and you skip all that to focus on reusability and ignore any kind of asynchronous cases where you don’t have tight control over the order the curried function receives arguments.