Implement sum, a curried adder that accepts numbers one call at a time and returns the running total when invoked with no arguments. This is the same family as classic currying, but with a twist: the chain has no fixed length. The wrapper keeps handing back a new function on every numeric call and only "settles" — produces a number — when you call it with (). The empty call is the terminator: a sentinel that means "no more numbers, give me the total."
Unlike curry(fn), there is no fn.length to drive the stopping decision. The caller decides when the chain ends, and the chain stays open for as long as numbers keep arriving. Your job is to keep the running total in a closure and react to the terminator.
// sum(1)(2)(3)() === 6
function sum(n?: number): number | ((next?: number) => number | Function);
// numeric call: returns another sum-shaped function
// empty call (): returns the running total as a number
// Basic chain — three numeric calls, then the terminator.
sum(1)(2)(3)(); // 6
sum(10)(20)(30)(); // 60
// Terminator returns the running total at the point you call it.
sum(5)(); // 5 — total after one number
sum(1)(2)(); // 3 — total after two numbers
// Immediate terminator. No numbers, no total — return 0.
sum(); // 0
() with zero arguments tells the chain to stop and return the running total. Until that call arrives, every step returns a new function.curry(fn), you do not know how many numbers will arrive. Don't try to count down from fn.length; let the terminator decide.sum() with no first argument returns 0. The terminator can fire on the very first call when there are no numbers to add.typeof sum(1) is 'function', and so is typeof sum(1)(2). Only the empty call produces a number.sum(1)(2) and then sum(1)(2) again must give the same total each time — no state should leak between separate chains.+ accumulator; treat all numeric inputs the same.You'll build a curried adder whose chain length is decided by the caller — every numeric call hands back a fresh function, and an empty call () collapses the chain to its running total.
Imagine a vending machine that takes coins one at a time. After each coin, the machine is still "open" — it waits for the next coin or a "checkout" button. Press checkout and it gives you the total. sum is that machine in function form: every numeric call drops a coin into the closure, and the empty call () is the checkout button. The trick is that the function doesn't know up front how many coins (numbers) you'll feed it — unlike classic currying, where the count is fixed by fn.length.
Picture a single bucket that holds the running total. Every numeric call rebuilds the bucket with a new total and hands it back, still callable. The empty call cracks the bucket open and gives you the number inside.
Two things make this different from arity-based curry. First, there is no fn.length to count down from — the implementation doesn't know how many numbers are coming. Second, the stopping rule is delegated to the caller: they signal "done" by passing no argument.
If you've written curry before, your instinct is to count arguments. Maybe collect them with rest params and check args.length:
function sumNaive(...args) {
// try to "be done" if no args came in
if (args.length === 0) return 0;
// …otherwise what? recurse with what?
return sumNaive(...args[0]);
}
This goes nowhere. Each call to sumNaive only sees the arguments of that one call — there's no place to accumulate across calls because the function isn't returning a new closure. sumNaive(1)(2)(3)() immediately blows up: sumNaive(1) returns sumNaive(...1) which is a number-spread error, not a function. Even if we patched the spread, we'd have no place to remember 1 for when 2 arrives later.
The mistake is reaching for variadic rest parameters when the structure we actually need is a chain of single-arg functions that each carry forward the total in a closure. The accumulator can't live in the call's parameter list — it has to live in scope.
function sum(n) {
// Immediate-terminator case: sum() with no first arg => running total is 0.
if (n === undefined) return 0;
// Otherwise, return a function that either takes the next number
// and recurses, or — when called with no args — returns the total.
return function inner(next) {
// Terminator: caller invoked us with no argument. Return what we have.
if (next === undefined) return n;
// Numeric step: fold next into the running total by re-entering sum
// with the new partial sum. This rebuilds the closure each step, so
// every partial chain has its own independent total.
return sum(n + next);
};
}
module.exports = { sum };
Three shifts from the naive version. First, the running total lives in the closure variable n, not in a parameter list — each call to sum captures one specific number, and the returned inner closes over it. Second, the empty call is detected by checking next === undefined; JavaScript fills in undefined for the parameter when the caller passes nothing, which is exactly the signal we need. Third, accumulation happens by re-entering sum(n + next) rather than mutating a shared variable. That recursive call returns a fresh closure with the new total — so any two partial chains built from the same prefix stay independent.
A subtlety worth calling out: we use recursion into sum itself instead of writing n = n + next and returning the same inner. The "mutate n" shortcut breaks the test reusing a partial chain produces the same total each time — once you mutate, calling sum(1)(2) twice would double-count the second 2 because both terminators see the same shared n. Recursion is what buys us referential transparency for partials.
There is a sibling implementation that uses a closure-held mutable accumulator and returns the same next function every step:
// Alternative — works but partials are NOT independent.
function sumMutable(n) {
if (n === undefined) return 0;
let total = n;
function next(x) {
if (x === undefined) return total;
total += x;
return next; // same function reference reused
}
return next;
}
This is shorter and slightly faster (one closure per chain, not one per step). But it fails the "reusing a partial chain" test the moment a caller does const partial = sumMutable(1)(2); partial(); partial(3)(); — the first terminator reads total === 3, the second numeric call adds 3 into the same total so the next terminator reads 6 instead of the expected fresh accumulation. The recursive version above is the right default because it preserves the invariant every closure represents exactly one snapshot of the running total. If you need raw speed and you can guarantee partials are never reused, the mutable variant is fine.
Trace sum(1)(2)(3)() step by step.
sum(1) — n is 1, not undefined, so we skip the immediate-zero branch. We return function inner(next) { ... } with n === 1 captured. The user sees a function back.sum(1)(2) — the inner function runs with next === 2. next is not undefined, so we fall through to the recursive branch: return sum(1 + 2), which is sum(3). That call returns a brand-new inner with n === 3 captured. The user sees another function.sum(1)(2)(3) — the new inner runs with next === 3. Again next is not undefined, so we return sum(3 + 3), which is sum(6). That returns yet another inner, this time with n === 6. Still a function.sum(1)(2)(3)() — the deepest inner runs with next === undefined (the caller passed nothing). The terminator guard fires: return n, which is 6. That 6 propagates all the way back up the call stack.Four function entries, three of which return a new closure, one of which returns a number. The chain ends exactly where the caller put the empty call.
Trace sum() for completeness. The very first guard hits: n === undefined, return 0. No closure is created. This is the immediate-terminator case — the chain ends before it begins.
undefined as a value." The terminator check is next === undefined. If a caller writes sum(1)(undefined), that triggers the terminator too — JavaScript can't distinguish "passed nothing" from "passed undefined." That's accepted as a design quirk; if you need to disambiguate, switch to checking arguments.length === 0.sum() must short-circuit to 0. Without the up-front guard, sum() would return function inner(...) and then sum()() would be required to get 0. The spec is one call: empty → zero. Fix: handle n === undefined at the top before constructing inner.let total = n; and total += next; inside inner, you save a function allocation per step — but two terminators on the same partial chain now read different values. The test reusing a partial chain produces the same total each time pins this. Fix: re-enter sum(n + next) and return its result; never mutate the captured total.return n + next; on the numeric branch — that ends the chain on the second call, so sum(1)(2) evaluates to 3 and sum(1)(2)(3) throws "3 is not a function." Numeric steps must always return a function; only the empty call returns the number.mul with the same shape. Swap + for * and initialise the closure with 1 instead of 0. The terminator pattern stays identical — mul(2)(3)(4)() === 24. This generalises to any associative binary operation: pass the operator and the identity element as parameters and you have a reducer-curry factory.chain(op, identity). const sum = chain((a, b) => a + b, 0); const mul = chain((a, b) => a * b, 1);. Useful when you want a small family of terminator-style accumulators without duplicating code.[1, 2, 3].reduce((a, b) => a + b, 0) is shorter and more familiar. Reach for terminators only when the chain is genuinely open-ended and the closing call adds enough clarity to justify the indirection.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
Implement sum, a curried adder that accepts numbers one call at a time and returns the running total when invoked with no arguments. This is the same family as classic currying, but with a twist: the chain has no fixed length. The wrapper keeps handing back a new function on every numeric call and only "settles" — produces a number — when you call it with (). The empty call is the terminator: a sentinel that means "no more numbers, give me the total."
Unlike curry(fn), there is no fn.length to drive the stopping decision. The caller decides when the chain ends, and the chain stays open for as long as numbers keep arriving. Your job is to keep the running total in a closure and react to the terminator.
// sum(1)(2)(3)() === 6
function sum(n?: number): number | ((next?: number) => number | Function);
// numeric call: returns another sum-shaped function
// empty call (): returns the running total as a number
// Basic chain — three numeric calls, then the terminator.
sum(1)(2)(3)(); // 6
sum(10)(20)(30)(); // 60
// Terminator returns the running total at the point you call it.
sum(5)(); // 5 — total after one number
sum(1)(2)(); // 3 — total after two numbers
// Immediate terminator. No numbers, no total — return 0.
sum(); // 0
() with zero arguments tells the chain to stop and return the running total. Until that call arrives, every step returns a new function.curry(fn), you do not know how many numbers will arrive. Don't try to count down from fn.length; let the terminator decide.sum() with no first argument returns 0. The terminator can fire on the very first call when there are no numbers to add.typeof sum(1) is 'function', and so is typeof sum(1)(2). Only the empty call produces a number.sum(1)(2) and then sum(1)(2) again must give the same total each time — no state should leak between separate chains.+ accumulator; treat all numeric inputs the same.You'll build a curried adder whose chain length is decided by the caller — every numeric call hands back a fresh function, and an empty call () collapses the chain to its running total.
Imagine a vending machine that takes coins one at a time. After each coin, the machine is still "open" — it waits for the next coin or a "checkout" button. Press checkout and it gives you the total. sum is that machine in function form: every numeric call drops a coin into the closure, and the empty call () is the checkout button. The trick is that the function doesn't know up front how many coins (numbers) you'll feed it — unlike classic currying, where the count is fixed by fn.length.
Picture a single bucket that holds the running total. Every numeric call rebuilds the bucket with a new total and hands it back, still callable. The empty call cracks the bucket open and gives you the number inside.
Two things make this different from arity-based curry. First, there is no fn.length to count down from — the implementation doesn't know how many numbers are coming. Second, the stopping rule is delegated to the caller: they signal "done" by passing no argument.
If you've written curry before, your instinct is to count arguments. Maybe collect them with rest params and check args.length:
function sumNaive(...args) {
// try to "be done" if no args came in
if (args.length === 0) return 0;
// …otherwise what? recurse with what?
return sumNaive(...args[0]);
}
This goes nowhere. Each call to sumNaive only sees the arguments of that one call — there's no place to accumulate across calls because the function isn't returning a new closure. sumNaive(1)(2)(3)() immediately blows up: sumNaive(1) returns sumNaive(...1) which is a number-spread error, not a function. Even if we patched the spread, we'd have no place to remember 1 for when 2 arrives later.
The mistake is reaching for variadic rest parameters when the structure we actually need is a chain of single-arg functions that each carry forward the total in a closure. The accumulator can't live in the call's parameter list — it has to live in scope.
function sum(n) {
// Immediate-terminator case: sum() with no first arg => running total is 0.
if (n === undefined) return 0;
// Otherwise, return a function that either takes the next number
// and recurses, or — when called with no args — returns the total.
return function inner(next) {
// Terminator: caller invoked us with no argument. Return what we have.
if (next === undefined) return n;
// Numeric step: fold next into the running total by re-entering sum
// with the new partial sum. This rebuilds the closure each step, so
// every partial chain has its own independent total.
return sum(n + next);
};
}
module.exports = { sum };
Three shifts from the naive version. First, the running total lives in the closure variable n, not in a parameter list — each call to sum captures one specific number, and the returned inner closes over it. Second, the empty call is detected by checking next === undefined; JavaScript fills in undefined for the parameter when the caller passes nothing, which is exactly the signal we need. Third, accumulation happens by re-entering sum(n + next) rather than mutating a shared variable. That recursive call returns a fresh closure with the new total — so any two partial chains built from the same prefix stay independent.
A subtlety worth calling out: we use recursion into sum itself instead of writing n = n + next and returning the same inner. The "mutate n" shortcut breaks the test reusing a partial chain produces the same total each time — once you mutate, calling sum(1)(2) twice would double-count the second 2 because both terminators see the same shared n. Recursion is what buys us referential transparency for partials.
There is a sibling implementation that uses a closure-held mutable accumulator and returns the same next function every step:
// Alternative — works but partials are NOT independent.
function sumMutable(n) {
if (n === undefined) return 0;
let total = n;
function next(x) {
if (x === undefined) return total;
total += x;
return next; // same function reference reused
}
return next;
}
This is shorter and slightly faster (one closure per chain, not one per step). But it fails the "reusing a partial chain" test the moment a caller does const partial = sumMutable(1)(2); partial(); partial(3)(); — the first terminator reads total === 3, the second numeric call adds 3 into the same total so the next terminator reads 6 instead of the expected fresh accumulation. The recursive version above is the right default because it preserves the invariant every closure represents exactly one snapshot of the running total. If you need raw speed and you can guarantee partials are never reused, the mutable variant is fine.
Trace sum(1)(2)(3)() step by step.
sum(1) — n is 1, not undefined, so we skip the immediate-zero branch. We return function inner(next) { ... } with n === 1 captured. The user sees a function back.sum(1)(2) — the inner function runs with next === 2. next is not undefined, so we fall through to the recursive branch: return sum(1 + 2), which is sum(3). That call returns a brand-new inner with n === 3 captured. The user sees another function.sum(1)(2)(3) — the new inner runs with next === 3. Again next is not undefined, so we return sum(3 + 3), which is sum(6). That returns yet another inner, this time with n === 6. Still a function.sum(1)(2)(3)() — the deepest inner runs with next === undefined (the caller passed nothing). The terminator guard fires: return n, which is 6. That 6 propagates all the way back up the call stack.Four function entries, three of which return a new closure, one of which returns a number. The chain ends exactly where the caller put the empty call.
Trace sum() for completeness. The very first guard hits: n === undefined, return 0. No closure is created. This is the immediate-terminator case — the chain ends before it begins.
undefined as a value." The terminator check is next === undefined. If a caller writes sum(1)(undefined), that triggers the terminator too — JavaScript can't distinguish "passed nothing" from "passed undefined." That's accepted as a design quirk; if you need to disambiguate, switch to checking arguments.length === 0.sum() must short-circuit to 0. Without the up-front guard, sum() would return function inner(...) and then sum()() would be required to get 0. The spec is one call: empty → zero. Fix: handle n === undefined at the top before constructing inner.let total = n; and total += next; inside inner, you save a function allocation per step — but two terminators on the same partial chain now read different values. The test reusing a partial chain produces the same total each time pins this. Fix: re-enter sum(n + next) and return its result; never mutate the captured total.return n + next; on the numeric branch — that ends the chain on the second call, so sum(1)(2) evaluates to 3 and sum(1)(2)(3) throws "3 is not a function." Numeric steps must always return a function; only the empty call returns the number.mul with the same shape. Swap + for * and initialise the closure with 1 instead of 0. The terminator pattern stays identical — mul(2)(3)(4)() === 24. This generalises to any associative binary operation: pass the operator and the identity element as parameters and you have a reducer-curry factory.chain(op, identity). const sum = chain((a, b) => a + b, 0); const mul = chain((a, b) => a * b, 1);. Useful when you want a small family of terminator-style accumulators without duplicating code.[1, 2, 3].reduce((a, b) => a + b, 0) is shorter and more familiar. Reach for terminators only when the chain is genuinely open-ended and the closing call adds enough clarity to justify the indirection.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.