cf.completefrontend

Write the codeMedium

Write sum so that sum(1)(2)(3)() returns 6: it keeps adding until it's called with no arguments.

Before you start

What's currying?

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):

CallArguments so farReturns
add3(1)1A function waiting for more
add3(1)(2)1, 2A function waiting for more
add3(1)(2)(3)1, 2, 36: it now has as many arguments as the original expects, so it calls it
add3(1, 2)(3)1, 2, 36
add3(1, 2, 3)1, 2, 36

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.

Each call can pass one or more numbers, 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.

Write this
function sum(...numbers) {
  // your code
}
 
// 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

Solve it in the editor, with tests

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…