Currying. Another in a long list of words that anyone outside of programming hears and just assumes you are making up. Currying is a way to partially apply a function. What the hell does that mean? It turns a function that takes two arguments into a function that takes one argument and returns a function which takes the other argument.
Wut.
Observe...
In javascript, instead of having this:
let add = function(x, y) {
return x + y
}
we could do this:
let add = function(x){
return function(y){
return x + y
}
}
We could then call it like so:
let addTen = add(10);
When we do that the 10
is passed in as x
:
let add = function(10){
return function(y){
return 10 + y
}
}
which means we are returned this function:
function(y) {
return 10 + y
}
So when you call addTen()
you are really calling:
function(y) { return 10 + y }
which means if you do this addTen(4)
it's the same as:
function(4) { return 10 + 4} // 14
So our addTen()
function always adds ten to whatever we pass in. We can make similar functions in the same way:
const addTwo = add(2)
// addTwo() will add two to whatever you pass inlet addSeventy = add(70) // ... and so on...
Curry! Huh! What is it good for...?
Okay so we get the gist of how currying works. But what on earth would we use it for?
Well it turns out to be good for several things. First of all, let’s write a function without currying.
const dinner = function(firstIngredient, secondIngredient, rest) {
return firstIngredient + secondIngredient + rest
}
And if we wanted to make dinner we would do this:
const tuesdaysDinner = dinner(chicken, rice, turmeric)
const wednesdaysDinner = dinner(beef, rice, turmeric)
Easy enough.
But we are lazy programmers. We hate repetition - we can do better! Rice and turmeric is time consuming to create. If we can create a massive batch of rice and turmeric, we could use it for both meals! But how could we do this....?
YOU GUESSED IT. With currying.
First of all let's turn our function that takes three arguments into a nested series of functions that all take one argument, and return a function.
const partiallyAppliedDinner = function(firstIngredient) {
return function(secondIngredient) {
return function(lastIngredient) {
return firstIngredient + secondIngredient + lastIngredient
}
}
}
Now we have curried our function, we can do this:
const prep = partiallyAppliedDinner(rice)
rice
will be passed in as firstIngredient
which means we are returned a function that looks like this:
function(secondIngredient) {
return function(lastIngredient) {
return rice + secondIngredient + lastIngredient
}
}
which means we can do this:
const preppedDinner = partiallyAppliedDinner(rice)(turmeric)
and turmeric
will be passed in as secondIngredient
.
This is amazing. Our preppedDinner
evaluates to a function that looks like this:
function(lastIngredient) {
return rice + turmeric + lastIngredient
}
So we can now do this:
const tuesdaysDinner = preppedDinner(chicken)
const wednesdaysDinner = preppedDinner(beef)
We now have the expensive rice
/ turmeric
operation memoized for re-use!
We have also discovered an abstraction. We could name this abstraction something like sideDish
. We reduced duplication of a common operation (the adding of rice
and turmeric
) by abstracting it up to a function of its own - our preppedDinner
variable. Crucially, notice how we didn't need to write any other functions to achieve this abstraction. Without currying we could have written two functions, an addRiceAndTurmeric()
and an addLastIngredient()
.Then we could have done some sort of addRiceAndTumeric(rice, tumeric) + addLastIngredient(chicken)
travesty. Observe:
const addRiceAndTurmeric = function(rice, turmeric){
return rice + turmeric
}const addLastIngredient = function(lastIngredient, rest){
return lastIngredient + rest
}const tuesdaysDinner = addLastIngredient(
chicken,
addRiceAndTurmeric(
rice,
turmeric
)
)const wednesdaysDinner = addLastIngredient(
beef,
addRiceAndTurmeric(
rice,
turmeric
)
)
Now let’s see that with currying:
const partiallyAppliedDinner = function(firstIngredient) {
return function(secondIngredient) {
return function(lastIngredient) {
return firstIngredient + secondIngredient + lastIngredient
}
}
}const turmericRiceAnd = partiallyAppliedDinner(rice)(turmeric)
const tuesdaysDinner = turmericRiceAnd(chicken)
const wednesdaysDinner = turmericRiceAnd(beef)
Summary
To summarise currying is the process of taking a function that takes one or more arguments and turning that into a series of functions that take one argument each, and each return a function. Currying allows us to:
- memoize an expensive operation
- calculate part of a function before we have all of the parameters available
- achieve abstractions in functional paradigms