Currying turns a function that takes several arguments, such as add3(a, b, c), into one you call one argument at a time: add3(a)(b)(c). Each call takes an argument and returns a new function waiting for the next. The call that brings the last argument runs the original function and returns its result. It's named after the logician Haskell Curry.
// A curried add, written by hand: each call returns a function waiting for the next argument
const add = (a) => (b) => a + b;
add(2)(3); // 5
const add5 = add(5); // a function waiting for b, with a fixed at 5
add5(1); // 6
add5(10); // 15: add5 can be reused, and nothing changes it
[1, 2, 3].map(add5); // [6, 7, 8]
That's what makes currying handy: a half-applied function such as add5 is a new, more specific function you can store, reuse, and pass to map, filter, or an event listener. You fix the argument that stays the same once, and supply the one that varies later.
Writing every function this way is tedious, so helpers such as Lodash's _.curry curry an ordinary function for you. A function's length property is the number of parameters it declares before the first one that has a default value or starts with ...: 3 for (a, b, c) => a + b + c, but 0 for (...nums) => 0. The curried version collects arguments until it has that many, then calls the original. Lodash's version also accepts several arguments in one call. With const add3 = _.curry((a, b, c) => a + b + c):
| Call | Arguments so far | Returns |
|---|---|---|
add3(1) | 1 | A function waiting for more |
add3(1)(2) | 1, 2 | A function waiting for more |
add3(1)(2)(3) | 1, 2, 3 | 6: it now has as many arguments as the original expects, so it calls it |
add3(1, 2)(3) | 1, 2, 3 | 6 |
add3(1, 2, 3) | 1, 2, 3 | 6 |
Two variants are worth knowing. Lodash's _.curry also accepts a placeholder, _, that holds a position open for a later call: with const subtract = _.curry((a, b) => a - b), subtract(_, 1) fixes b and waits for a, so subtract(_, 1)(10) is 9. And when a function can't know how many arguments are coming, such as a sum that adds up whatever it's given, the caller ends the chain with an empty call: sum(1)(2)(3)() returns 6.
Currying is close to partial application, which fixes some arguments in one step and returns a function that takes all the rest in its next call. A curried function keeps returning functions until it has every argument, so you can fill them in a piece at a time.
Write sum(...numbers). Called with one or more numbers, it returns a function that takes more numbers the same way. Called with no arguments, it returns the total of every number passed so far in the chain, and the chain ends.
A call can pass several numbers at once, so sum(1, 2)(3)() is also 6. sum() on its own returns 0.
A chain stopped partway can be reused: after const add5 = sum(5), add5(1)() and add5(10)() both work, and neither affects the other. Every new chain starts from 0.
sum(1)(2)(3)(); // 6
sum(1, 2)(3, 4)(); // 10
sum(); // 0
const add5 = sum(5);
add5(1)(); // 6
add5(10)(); // 15, not 16: add5 still adds to 5
add5(); // 5
expect(sum(1)(2)(3)()).toBe(6);
expect(sum(5)()).toBe(5);
expect(typeof sum(1)).toBe("function");
expect(typeof sum(1)(2)).toBe("function");
expect(typeof sum(1, 2)(3, 4)).toBe("function");
expect(sum(1, 2)(3, 4)()).toBe(10);
expect(sum(1, 2, 3)()).toBe(6);
expect(sum(1)(2, 3, 4)()).toBe(10);
expect(sum()).toBe(0);
expect(sum(0)(5)()).toBe(5);
expect(sum(2)(-2)(3)()).toBe(3);
expect(sum(-2)(2)(0)()).toBe(0);
const add5 = sum(5);
expect(add5(1)()).toBe(6);
expect(add5(10)()).toBe(15);
expect(add5()).toBe(5);
const three = sum(1)(2);
const six = three(3);
const thirteen = three(10);
expect(six()).toBe(6);
expect(thirteen()).toBe(13);
expect(three()).toBe(3);
expect(six(1)()).toBe(7);
expect(six()).toBe(6);
expect(sum(1)(2)()).toBe(3);
expect(sum(4)()).toBe(4);
expect(sum(10)(20)()).toBe(30);
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Infinite curry sum. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.