The currying is a technique that transforms a function with multiple parameters into a sequence of functions, each with a single parameter.
In other words, instead of passing all arguments to the function at once, currying allows you to “decompose” the function into simpler steps.
Currying is a concept that comes from the mathematical field and has found an important place in functional programming. In JavaScript, it has become a very popular pattern.
This may seem complex at first. However, at times, this approach allows for improved modularity and code reuse.
Currying in JavaScript
To illustrate currying, let’s consider a simple example. Imagine we have a simple function that adds two numbers:
function add(a, b) {
return a + b;
}Nothing particularly striking, right? Let’s see the curried version of this function.
function curriedAdd(a) {
return function(b) {
return a + b;
};
}Now, instead of calling our function add(a, b), we have a function that accepts one parameter and returns another function that accepts another parameter.
We can use our curriedAdd version as follows:
const add5 = curriedAdd(5);
console.log(add5(3)); // Output: 8
console.log(add5(10)); // Output: 15In this example,
curriedAddtakes an argumentaand returns a new function that expects the second argumentb.- This allows us to create specialized functions (like
add5) that always add a fixed number.
That is, we now have a function that generates functions that add a specific number.
Currying with Lambdas
We can use lambda functions to create a more compact version of currying. The behavior remains the same, but using lambdas makes it more concise and more in line with modern JavaScript style.
const add = a => b => a + b;
const result = add(2)(3); // Result: 5In this example,
- The
addfunction is now defined as a series of nested functions using arrow syntax. - Each function returns another function that accepts the next argument until the final calculation with
a + bis reached.
Implementation of Currying
We have seen how to manually convert a function into a “curried” version, as in the previous example with the add function.
However, it is common to create some generic solution to convert any function. Let’s look at some of them.
Utility Function
Currying can be implemented with a simple utility function. For example:
function currying(fn) {
return function(arg1) {
return function(arg2) {
return fn(arg1, arg2);
};
};
}
// Usage
function multiply(a, b) {
return a * b;
}
const curriedMultiply = currying(multiply);
const multiplyBy2 = curriedMultiply(2);
console.log(multiplyBy2(4)); // Output: 8In this example, currying converts a function with two arguments into a sequence of unary functions.
Generic Utility Function
We can also create a utility function for more advanced currying, which allows for an arbitrary number of arguments.
function currying(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return function(...args2) {
return curried(...args, ...args2);
};
}
};
}
// Usage
function multiply(a, b, c) {
return a * b * c;
}
const curriedMultiply = currying(multiply);
const multiplyBy2and3 = curriedMultiply(2)(3);
console.log(multiplyBy2and3(4)); // Output: 24In this example, the currying function takes any function fn and returns a curried version of it. This allows arguments to be passed one at a time or in groups.
Using Libraries
Many JavaScript libraries and frameworks provide support for currying. For example, Lodash has a function _.curry that makes it easy to create curried functions.
const _ = require('lodash');
const curriedSum = _.curry(function(a, b, c) {
return a + b + c;
});
console.log(curriedSum(1)(2)(3)); // 6Function Composition
Currying and function composition are closely related. While currying decomposes functions into a series of unary functions, function composition allows you to combine them to build new, more complex functions.
const add = a => b => a + b;
const multiply = a => b => a * b;
// We combine the functions to create a new one
const multipliedSum = (a, b, c) => multiply(a)(add(b)(c));
console.log(multipliedSum(2, 3, 4)); // Output: 14In this example,
addandmultiplyare curried functions that return a new function when receiving an argument.multipliedSumuses composition. First,add(b)(c)calculates the sum ofbandc, returning7. Then,multiply(a)(7)multiplies the result bya, giving14.
That is, we have composed two curried functions (add and multiply) to form a new, more complex function (multipliedSum).
In a sense, function composition is the inverse of currying: instead of splitting, we are combining.
Practical Examples
Discount Calculation
Let’s look at a practical example of how we could use currying in a real application:
function discount(percentage) {
return function(price) {
return price * (1 - percentage);
};
}
const applyDiscount10 = discount(0.1);
const applyDiscount20 = discount(0.2);
console.log(applyDiscount10(100)); // Result: 90
console.log(applyDiscount20(100)); // Result: 80In this example, discount is a curried function that takes a discount percentage and returns another function that takes the price and calculates the price with the discount applied.
Function Configuration
Currying is useful for configuring functions with default arguments.
function configureGreeting(greeting) {
return function(name) {
return `${greeting}, ${name}!`;
};
}
const formalGreeting = configureGreeting('Good morning');
console.log(formalGreeting('John')); // Good morning, John!Function Composition
Currying facilitates function composition, where multiple functions are combined to form a new function.
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
const curriedSum = currying(add);
const curriedMultiply = currying(multiply);
const result = curriedMultiply(curriedSum(2)(3))(4);
console.log(result); // 20In this example, curriedSum(2)(3) returns 5, and curriedMultiply(5)(4) returns 20.