Functional Concepts For JavaScript Developers: Currying

in #javascript8 years ago (edited)

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, "Mostly Adequate Guide to Functional Programming" 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. 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"));

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

  • 1. isAnimal expects one argument, the type
  • 2. isAnimal then returns a whole new function
  • 3. The returned function is used as the callback to .filter()
  • 4. Closure then allows our returned function to access the type variable which finally allows us to perform our equality comparisonAgain, 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.

Sort:  

Hi! I am a content-detection robot. This post is to help manual curators; I have NOT flagged you.
Here is similar content:
http://www.tuicool.com/articles/6B3Ebam

Nice. That's not that complicated! :)