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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // Output: 5
function add(a, b) { return a + b; } console.log(add(2, 3)); // Output: 5
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function add(a) {
return function(b) {
return a + b;
};
}
console.log(add(2)(3)); // Output: 5
function add(a) { return function(b) { return a + b; }; } console.log(add(2)(3)); // Output: 5
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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Curried function for addition
function add(a) {
return function(b) {
return a + b;
};
}
console.log(add(2)(3)); // Output: 5
// Curried function for addition function add(a) { return function(b) { return a + b; }; } console.log(add(2)(3)); // Output: 5
// 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function multiply(a, b, c) {
return a * b * c;
}
const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // Output: 24
function multiply(a, b, c) { return a * b * c; } const curriedMultiply = curry(multiply); console.log(curriedMultiply(2)(3)(4)); // Output: 24
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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"
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"
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const multiply = a => b => c => a * b * c;
console.log(multiply(2)(3)(4)); // Output: 24
const multiply = a => b => c => a * b * c; console.log(multiply(2)(3)(4)); // Output: 24
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const add5 = add(5); // Partial application
console.log(add5(10)); // Output: 15
const add5 = add(5); // Partial application console.log(add5(10)); // Output: 15
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const multiplyBy2 = multiply(2);
const multiplyBy3 = multiply(3);
console.log(multiplyBy2(4)); // Output: 8
console.log(multiplyBy3(4)); // Output: 12
const multiplyBy2 = multiply(2); const multiplyBy3 = multiply(3); console.log(multiplyBy2(4)); // Output: 8 console.log(multiplyBy3(4)); // Output: 12
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.
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function subtract(a, b) {
return a - b;
}
const partialSubtract = (a) => (b) => subtract(a, b);
const subtract5 = partialSubtract(5);
console.log(subtract5(10)); // Output: -5
function subtract(a, b) { return a - b; } const partialSubtract = (a) => (b) => subtract(a, b); const subtract5 = partialSubtract(5); console.log(subtract5(10)); // Output: -5
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.