April 26, 2019

Just Enough FP: Currying

edit

Currying is, by far, one of the coolest things I’ve learned in the last few years. When it clicked in my brain, it was such an intense epiphany that I literally ran to the other room and begged my wife to join me in the office so I could explain it to her on the whiteboard. She doesn’t know a thing about programming, but she listened to me. “Yeah, Kyle, that kinda makes sense.” Ha. I just had to share it with the first person I could find. Bless her for putting up with me.

Anecdote over, on with the blog post!

What is Currying?

My least jargon filled way of answering that question is this: Currying is the act of refactoring a function that normally receives all its arguments at once into a series of functions that only take one argument at a time.

In JavaScript, functions can receive any number of arguments. The number of arguments a function expects to receive is known as its arity. Normally, we do not worry too much about how many arguments a function receives. If it needs more than one argument, we write it so that it receives all its arguments at one time. I believe this makes a lot of intuitive sense and maps to how our brains model and understand functions. Give the function all the arguments, do something with them, give me back the result. Easy peasy. But it’s not the only way.

In functional programming, all functions only receive one argument at a time. A function with an arity of one is known as a unary function. All curried functions are unary functions. A curried function that requires more than one argument to fulfill its operation, returns a new unary function with each argument until it has all the arguments it needs to finally evaluate.

Let me demonstrate the currying process with the canonical example, an add function.

// A common add function
function add(x, y) {
  return x + y
}

add(2, 3) // 5

This function has an arity of two, aka a binary function. It expects both arguments at the same time, adds them together, and gives the result back to you. To make this a curried function, we are going to refactor it so it receives the x and y arguments one at a time.

// A curried add function
function add(x) {
  return function (y) {
    return x + y
  }
}

// Or its arrow function equivalent
const add = x => y => x + y

// If we only have our `x` argument, we can apply it and return a
// function awaiting the `y` argument with the `x` in closure
const add2 = add(2)

// We can then supply the 'y' argument
add2(3) // 5

// For good measure, if you had both arguments at the same time,
// You can supply both by immediately invoking the returned function
add(4)(6) // 10

Our curried function receives the x and y arguments one at a time. When we receive the x argument, we return a new function awaiting the y. This new function has our x value stored in closure. This is known as partial application. Partial application is a very powerful feature of currying that I’ll explore in more detail in the next post, but for now just keep following along and try to grok it through context. It’ll be ok.

If you’re following with me, you now understand what currying is, but are probably still confused as to why this is an important technique. I’m going to give you an example that admittedly will skip ahead a bit, but should show you some of what gets unlocked when functions are curried.

Let’s consider the array method filter. filter receives a predicate function (a function that returns a boolean) that operates on each item in an array and returns a new array with only the items that return true. You can check out the docs here. What if we could use the same filter on multiple arrays more easily? Let’s create our own filter function that’s curried.

const filter = predicate => array => array.filter(predicate)

Our function receives the predicate argument first, returns a new function that awaits the array and then evaluates and returns the result. Let’s create a predicate and pass it to filter.

const filter = predicate => array => array.filter(predicate)
const filterForEvens = filter(x => x % 2 === 0)

We have now created a new function, filterForEvens which has the predicate partially applied. This predicate returns true if dividing by two returns a remainder of 0. The filterForEvens function awaits an an array argument. So let’s give it some arrays.

const filter = predicate => array => array.filter(predicate)
const filterForEvens = filter(x => x % 2 === 0)

const arr1 = [1, 2, 3, 4, 5, 6, 7]
const arr2 = [1, 4, 9, 16, 25, 36, 49]
const arr3 = [1, 8, 27, 64, 125, 216, 343]

filterForEvens(arr1) // [2, 4, 6]
filterForEvens(arr2) // [4, 16, 36]
filterForEvens(arr3) // [8, 64, 216]

By currying our filter function, we’ve given ourselves the opportunity to delay supplying the array argument. This allows us to supply as many different arrays to our filterForEvens function as we want and we never have to resupply the predicate argument.

As we’ll see in the next post, currying and partial application is a powerful combination. We can DRY up code in our application through it’s use. We’ll also explore two other topics related to currying: argument order and function composition.


In this series, I’m going over material from my Just Enough Functional Programming in JavaScript course on egghead. This post is based on the lesson Refactor a Function to Use Currying in JavaScript.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Need help with your software problems?

My team and I are ready to help you. Hire Agathist to build your next great project or to improve one of your existing ones.

Get in touch
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Agathist
Good software by good people.
Visit https://agath.ist to learn more
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.
Logo for Introduction to State Machines and XState
Introduction to State Machines and XState
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.