Learnitweb

Currying in JavaScript

1. Introduction

Currying is a functional programming technique where a function that takes multiple arguments is transformed into a series of functions, each taking a single argument. This allows you to apply arguments to a function one at a time.

In JavaScript, currying allows you to partially apply a function. In simple terms, you can provide a function with fewer arguments than it expects, and it will return another function that takes the remaining arguments. This technique is commonly used for reusability and flexibility in code.

2. What is Currying?

In traditional functions, you pass all the arguments at once:

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

console.log(add(2, 3)); // Output: 5

With currying, the function is transformed so that each argument is passed one by one:

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

console.log(add(2)(3)); // Output: 5

The function add first accepts a, then returns another function that accepts b and performs the operation.

3. Why Use Currying?

Currying is useful for several reasons:

  • Partial Application: You can pre-configure functions with some arguments, and they can be reused later with fewer arguments.
  • Reusability: You can build specialized functions from generic ones. Currying allows creating customized versions of a function by applying arguments step by step.
  • Functional Programming: Currying aligns well with functional programming techniques, where functions are treated as first-class citizens and can be composed.
  • Cleaner Code: It can reduce repetition and improve code clarity, especially when functions are dealing with configuration-like arguments.

4. How to Implement Currying in JavaScript

Let’s start by implementing a simple currying function.

Basic Example: Adding Two Numbers

// Curried function for addition
function add(a) {
    return function(b) {
        return a + b;
    };
}

console.log(add(2)(3)); // Output: 5

Here, the function add takes one argument a and returns another function that takes the second argument b and returns the sum of both.

Generalizing the Currying Function

You can create a more generic currying function that can handle multiple arguments:

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn(...args); // Apply the function when all arguments are received
        } else {
            return function(...next) {
                return curried(...args, ...next); // Collect more arguments
            };
        }
    };
}

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

const curriedAdd = curry(add);
console.log(curriedAdd(2)(3)); // Output: 5

In the above code:

  • curry(fn) is a higher-order function that takes a function fn as an argument.
  • It returns a curried function that collects arguments and invokes fn once all arguments are received.
  • This generic curried function can be used with any function, not just addition.

5. Currying for More Than Two Arguments

Currying can work for functions with more than two arguments. Here’s an example:

function multiply(a, b, c) {
    return a * b * c;
}

const curriedMultiply = curry(multiply);

console.log(curriedMultiply(2)(3)(4)); // Output: 24

In this case, multiply is a function that takes three arguments, and we use the curried version to pass each argument one by one.

6. Real-World Example: Curried Function for Configurations

Let’s consider a more practical example where currying is useful — creating configuration functions.

function createConfig(hostname) {
    return function(port) {
        return function(protocol) {
            return `${protocol}://${hostname}:${port}`;
        };
    };
}

const config = createConfig('localhost')(8080)('http');
console.log(config); // Output: "http://localhost:8080"

Here, we’ve created a curried function createConfig that configures a URL by passing hostname, port, and protocol step by step. Each part of the configuration is supplied one by one, making the code modular and reusable.

7. Example of Currying with ES6 Arrow Functions

In modern JavaScript (ES6 and later), you can use arrow functions to make currying more concise:

const multiply = a => b => c => a * b * c;

console.log(multiply(2)(3)(4)); // Output: 24

This is essentially the same as the previous example but written more compactly using arrow functions.

8. Benefits of Currying

Partial Application:

Currying allows you to fix some arguments of a function and reuse it with other arguments later.

const add5 = add(5);  // Partial application
console.log(add5(10)); // Output: 15

Reusability:

Instead of calling add(5)(x), you can call add5(x) every time, making the function more reusable.

Functional Composition:

Currying supports functional composition, allowing you to chain multiple curried functions together.

const multiplyBy2 = multiply(2);
const multiplyBy3 = multiply(3);
console.log(multiplyBy2(4)); // Output: 8
console.log(multiplyBy3(4)); // Output: 12

9. How Currying Differs from Partial Application

While currying and partial application seem similar, they are different concepts:

  • Currying: Transforms a function that takes multiple arguments into a series of unary (single argument) functions.
  • Partial Application: Fixes a specific number of arguments of a function, creating a new function that takes the remaining arguments.
function subtract(a, b) {
    return a - b;
}

const partialSubtract = (a) => (b) => subtract(a, b);
const subtract5 = partialSubtract(5);
console.log(subtract5(10)); // Output: -5

10. Conclusion

Currying is a powerful technique in JavaScript that helps write more modular, reusable, and concise code. It is especially useful in functional programming patterns where functions are treated as first-class citizens. By using currying, you can break down complex functions into smaller, single-argument functions, which can then be applied in a flexible way.

  • Currying is useful when you want to pre-configure functions.
  • Partial application can also be done with currying, allowing you to reuse the functions with some predefined arguments.

Currying can be applied to any function with multiple parameters, and it plays an important role in functional programming, making it easier to manage and apply functions dynamically.