Understanding Currying in JavaScript

Understanding Currying in JavaScript

Introduction

While talking about functional programming, we might have heard the term "currying". But what is it? What does it mean? How does it work? Let's find out.

What is currying?

Currying is a technique of evaluating a function with multiple arguments, into a sequence of functions with a single argument. It is also known as partial function application. It is not only used in functional programming, but also in object-oriented programming. It is used to make functions more flexible and reusable.

Let's take a look at an example to understand how currying works.

// curry(f) does the currying transform
function curry(fn) { 
  return function(a) {
    return function(b) {
      return fn(a, b);
    };
  };
}
// let curry = fn => (a => (b => fn(a,b)));

function add(a, b) {
  return a + b;
}

add(2,3); // 5
add(2,6); // 8

let curryAdd = curry(add);

curryAdd(1)(2); // 3

const add2 = curryAdd(2);
add2(3); // 5
add2(6); // 8

const add5 = curryAdd(5);
add5(7); // 12

In the above example, the function add is a regular function that takes 2 arguments and returns the sum. If we were to use that in multiple places with multiple combinations we'd have to define both arguments for every single function call which would be tedious. But what happens if we curry the function?

With currying, we can create an intermediate function, add2 or add5 that can be used to get our outputs in difference cases.

With currying, we are able to save the intermediate function as a specialized function that can be used again later.

The function f(a,b) was simplified to f(a)(b).

Why do we need currying?

Currying helps us to make functions more reusable and flexible. Something every developer should follow while writing code.

When we curry a function we are able to save the intermediate functions that can be used later. If a function that takes many arguments is curried, then every time we pass an argument to an intermediate function we get a more specialized function.

Let's say we want to build a translator:

function translate(fromLanguage, toLanguage, message) {
  ...
}

translate = _.curry(translate);

After that translate works normally:

translate("English", "Japanese", "Hello"); // Konichiwa

But also works in the curried for, so we can save the intermediate functions which can be reused

let translateEnglish = translate("English");

translateEnglish("Japanese", "Hello"); // Kinochiwa
translateEnglish("French", "Hello"); // Bonjour

let englishToJapanese = translateEnglish("Japanese");
englishToJapanese("Goodbye"); // Sayonara

So:

  • We still preserve the original function
  • We are able to generate partial functions easily that can be reused

Currying can also be used in composing functions.

Advanced implementation of curry

This implementation can be used for multi-argument functions

const curry = (fn) => {
    return helper = (...args) => {
        if (args.length >= fn.length) {
            return fn(...args);
        } else {
            return (...args2) => {
                return helper(...args, ...args2);
            };
        }
    };
};

Example implementation

function add(num1, num2, num3) {
    return num1 + num2 + num3;
}
let curryAdd = curry(add);

console.log(curryAdd(1, 2, 3)); // 6
console.log(curryAdd(1)(2, 3)); // 6
console.log(curryAdd(1)(2)(3)); // 6

Conclusion

While currying may not be commonly used and can feel complicated, it is an important concept to understand when it comes to functional programming.

Thanks for reading this article, and please feel free to leave any comments you have. I’m open to learning from you. Cheers!