The shape of a curried function
calculate(a, b, c)
becomes
calculate(a)(b)(c)
The behavior can stay the same. Only the way arguments arrive changes.
A small example
// Regular function
function add(a, b) {
return a + b;
}
add(2, 3); // 5
// Curried function
function curriedAdd(a) {
return function (b) {
return a + b;
};
}
curriedAdd(2)(3); // 5
curriedAdd(2)runs.It returns a new function and remembers
a = 2.- The returned function receives
3.It can still read
athrough a closure. - The final expression returns
5.Each pair of parentheses is a separate function call.
The same function with arrow syntax
Arrow functions make the nested shape compact. Read the arrows as "return another function."
const add = a => b => a + b;
const addTen = add(10);
addTen(5); // 15
addTen(20); // 30
Why this is useful: the first call configures a reusable function. Later calls only provide the value that changes.
Build reusable predicates
A curried comparison can capture a property name and expected value, then work directly as an array callback.
const hasValue = key => expected => item =>
item[key] === expected;
const isActive = hasValue("status")("active");
const activeUsers = users.filter(isActive);
The final function has the callback shape that filter needs. The configuration has already been supplied.
A generic curry helper
This helper keeps collecting arguments until it has at least as many as the original function declares.
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...nextArgs) =>
curried.apply(this, [...args, ...nextArgs]);
};
}
const sum3 = (a, b, c) => a + b + c;
const curriedSum = curry(sum3);
curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3); // 6
fn.length only counts parameters before the first default parameter, and it does not count a rest parameter. Production helpers need an explicit arity when those cases matter.
When currying earns its keep
- You repeatedly reuse the same configuration.
Capture the stable argument once and create a focused function for changing values.
- You compose small functions.
One-argument functions fit naturally into pipelines where each return value becomes the next input.
- The call reads more clearly.
hasRole("admin")(user)can express a configured test. If the nesting makes readers pause, a named factory function may be better.
Check your understanding
-
What does the first call to
multiply(4)(5)return ifmultiply = a => b => a * b?Show answer
multiply(4)returns a function that remembersa = 4. The second call suppliesb = 5. -
When does the generic
curryhelper call the original function?Show answer
It calls the original function when it has collected at least as many arguments as
fn.lengthreports. -
What JavaScript feature lets a returned function remember earlier arguments?
Show answer
A closure. The inner function keeps access to bindings from the scope where it was created.