Currying and Partial Application in JavaScript
This article provides a concise overview of currying and partial function application in JavaScript, explaining their core mechanics and practical differences. You will learn how currying transforms a multi-argument function into a chain of unary functions and how this behavior serves as a powerful foundation for partial application to create modular, reusable code.
What is Currying?
Currying is a functional programming technique where a function with multiple arguments is transformed into a sequence of functions, each taking a single argument.
Instead of taking all arguments at once like f(a, b, c),
a curried function is evaluated as f(a)(b)(c).
Standard Function vs. Curried Function
// Standard function
function add(a, b, c) {
return a + b + c;
}
add(1, 2, 3); // 6
// Curried function
function curriedAdd(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
curriedAdd(1)(2)(3); // 6
// Arrow syntax for currying
const arrowCurriedAdd = a => b => c => a + b + c;What is Partial Function Application?
Partial function application (or partial application) is the process of fixing a certain number of arguments to a function, producing a new function of smaller arity (accepting fewer arguments).
For example, if you have a function that takes three arguments and you provide two, partial application returns a function that only expects the remaining one argument.
function multiply(a, b) {
return a * b;
}
// Partial application using bind
const double = multiply.bind(null, 2);
console.log(double(5)); // 10How Currying Facilitates Partial Application
Currying natively enables partial application without the need for
methods like bind(). Because a curried function returns a
new function at every step of argument evaluation, you can stop the
chain at any point to create a specialized function.
Practical Example: Discount Calculator
Consider a function that calculates the final price of an item given a discount rate and the item’s price:
// Curried discount calculator
const applyDiscount = discount => price => price - price * discount;
// Partially apply the discount to create specialized functions
const tenPercentDiscount = applyDiscount(0.10);
const twentyPercentDiscount = applyDiscount(0.20);
// Use the partially applied functions
console.log(tenPercentDiscount(100)); // 90
console.log(tenPercentDiscount(50)); // 45
console.log(twentyPercentDiscount(100)); // 80In this example, applyDiscount is a curried function.
Calling applyDiscount(0.10) performs partial application by
fixing the discount parameter and returning a reusable function tailored
for 10% discounts.
Creating a Generic Curry Utility
In standard JavaScript libraries like Lodash or Ramda, curry functions are flexible enough to accept arguments either individually or in batches until the expected number of parameters (arity) is satisfied.
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
}
};
}
// Example usage:
function sendRequest(protocol, domain, path) {
return `${protocol}://${domain}/${path}`;
}
const curriedRequest = curry(sendRequest);
// Partial application by supplying arguments in different steps
const httpsRequest = curriedRequest('https');
const localHttpsRequest = httpsRequest('localhost:3000');
console.log(localHttpsRequest('api/users')); // "https://localhost:3000/api/users"
console.log(curriedRequest('http')('example.com')('home')); // "http://example.com/home"Summary of Differences
- Currying: Always decomposes a function into a unary chain (functions that take exactly one argument at a time).
- Partial Application: Fixes one or more arguments to a function immediately, returning a function that accepts the remaining arguments (which may still be multiple).
- Relationship: Currying provides an inherent mechanism for partial application because each returned function in the curried chain represents a partially applied state.