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: 15
In this example,
curriedAdd
takes an argumenta
and 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: 5
In this example,
- The
add
function 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 + b
is 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: 8
In 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: 24
In 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)); // 6
Function 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: 14
In this example,
add
andmultiply
are curried functions that return a new function when receiving an argument.multipliedSum
uses composition. First,add(b)(c)
calculates the sum ofb
andc
, 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: 80
In 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); // 20
In this example, curriedSum(2)(3)
returns 5, and curriedMultiply(5)(4)
returns 20.