Functional Concepts For JavaScript Developers: Currying



Curry spices

Before I understood the concept of currying, I was always filled with anxiety when hearing other developers describe it. They always made it seem so complicated and difficult to understand; as if an ominous thundercloud arrived every time the topic was mentioned. Well I assure you that the concept of currying, at least applied to programming, is not hard to understand at all. It’s actually quite easy to grasp and once you do, opens the door to a powerful functional programming technique.

The Core Idea

Let’s first start with a definition from the great book by Brian Lonsdorf

“The concept is simple: You can call a function with fewer arguments than it expects. It returns a function that takes the remaining arguments.”

In other words, a curry function takes and provides additional arguments over time. It does this by returning additional functions to do the work for us.1 In order to see how this works, let’s take a normal function and curry it.

var add = function(x, y) {
  return x + y;
}

add(10, 20); // 30

Here we have a function that takes two numbers and returns the sum. Simple. Let’s curry it.

var add = function(x) {
  return function(y) {
    return x + y;
  }
}

var addFifty = add(50);

addFifty(10); // 60

Instead of returning the immediate result, we’re returning a function that acts as a closure over variable x. This allows us to keep a reference to that value when we execute the returned function addFifty at a later time. Finally, our returned function provides us with the summation of both numbers. In this example we would say the add function is “curried”.

Why is Currying useful?

Curry functions are useful for many reasons. From a functional programming perspective, they allow your program to become “pure”. I’ll be writing more about pure functions at a later time but suffice it to say for now, pure functions allow your application to be better understood by yourself and other team members.

A more applicable reason can be shown by demonstrating a less contrived example. While adding two numbers together is great for explaining a new concept, it doesn’t immediately highlight the enormous benefits of using it within the context of a larger application. Let’s do that now.

Let’s assume we’re working with a list of animals like the below:

const animals = [
  {
    id: 1,
    name: "Tails",
    type: "Cat",
    adoptable: false
  },
  {
    id: 2,
    name: "Soul",
    type: "Cat",
    adoptable: true
  },
  {
    id: 3,
    name: "Fred",
    type: "Dog",
    adoptable: true
  },
  {
    id: 4,
    name: "Fury",
    type: "Lion",
    adoptable: true
  }
];

We need a way to filter down these animals based on type. We could do something like this:

animals.filter(animal =>
   animal.type === "Cat"
);

/*
[{
   adoptable: false,
   id: 1,
   name: "Tails",
   type: "Cat"
 },
 {
   adoptable: true,
   id: 2,
   name: "Soul",
   type: "Cat"
 }];
*/

Looks pretty good. This grabs all of the cats in the list. However there’s a better way to write the same thing.

One major drawback to the above code is that the type of animal is tightly coupled to the act filtering itself. This prevents reusability and encourages the cultivation of our worst enemy: state.

Let’s see if we can do better.

const isAnimal = 
   type => 
     animal =>
       animal.type === type

animals.filter(isAnimal("Cat"));
 
/*
 [{
   adoptable: false,
   id: 1,
   name: "Tails",
   type: "Cat"
 },
 {
   adoptable: true,
   id: 2,
   name: "Soul",
   type: "Cat"
 }];
 */

Much cleaner. Let’s break this down into steps.

  • isAnimal expects one argument, the type
  • isAnimal then returns a whole new function
  • The returned function is used as the callback to .filter()
  • Closure then allows our returned function to access the type variable which finally allows us to perform our equality comparison

Again, why is this better than the previous example? As an application grows in size, it becomes harder to understand what functions are doing what and where they’re coming from within the codebase. Striving to write pure functions like in our solution eliminates the state chase, and makes our entire app more testable by breaking things into single responsibilities.

Conclusion & Resources

I hope this inspired you to see the powerful nature behind curry functions. I’ve personally found them very useful and really inspired me to learn more about functional programming concepts.

If you’re interested in further learning, I encourage you to checkout functional libraries like Ramda.js or lodash which contain general curry functions that can help build some really exciting functionality.

Cheers.

  • David Chase

    Hmm interesting article… though i think it would make more intuitive to say “why currying” is useful based on what you are describing in your examples… rather than simply stating they make your program “pure” which isn’t a guarantee since you can easily curry a function that performs a side-effect and therefore is no longer pure.

    You examples show that currying is useful for creating building blocks of reusable functions. For instances you can further your example by doing something like so

    const filter = curry((fn, list) => list.filter(fn));
    const getCats = filter(isAnimal('cats'));

    so then getCats is a function that has been partially applied and is simply waiting for data to be passed in. Which would also be nice to show the relationship between partial application and currying.

    Also i believe the library is called Ramda.js

    • Hey David,

      Thanks for your valuable feedback. I’m studying partial application right now so this very useful! Also thanks for finding the typo.

  • Tracy-Gregory Gilmore

    Andrew, Your elegant article provides a very effective demonstration of how currying can simplify even the most basic of code such as a filter predicate. I executed your example myself and found a small typo –

    animals.filter(animal =>
    animal.type === “Cat”;
    );

    does not require a semi-colon after “Cat” as the expression is a parameter to the filter method.
    Thanks.

    • Thanks for your feedback Tracy! I’ve gone ahead and fixed that typo. Cheers.