Currying in javascript

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 in
let 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:

  1. memoize an expensive operation
  2. calculate part of a function before we have all of the parameters available
  3. achieve abstractions in functional paradigms