Back to JavaScript
JavaScript
medium
mid

How would you implement currying for an infinite sum like sum(10)(20)(30)()?

Return a function that either accepts another argument and returns itself, or returns the running total when called with no argument. Implementation hinges on closure + a base case.

5 min read·~15 min to think through

This question — "implement sum(10)(20)(30)() === 60" or its sibling mul(2)(3)(4) === 24 — is one of the most common JavaScript closure/currying interview prompts. The interviewer is checking three things: (1) do you understand closures, (2) can you design a function that has two distinct behaviors depending on how it's called, (3) can you discuss the trade-offs of multiple solutions. There are two canonical patterns and one "showy" advanced one.

What "currying" actually means. Currying decomposes a function of N arguments into a chain of N functions of 1 argument each: f(a, b, c)f(a)(b)(c). Each call captures one argument in a closure and returns a new function waiting for the next. Infinite currying extends this idea to a variable number of calls, with a termination signal.

Approach 1: terminate with an empty call ().

The returned function is dual-purpose: called with an argument, it accumulates and returns itself; called without an argument, it returns the running total.

ts
function sum(a: number) {
  let total = a;
  function next(b?: number): number | typeof next {
    if (b === undefined) return total;
    total += b;
    return next;
  }
  return next;
}

sum(10)(20)(30)();             // 60
sum(1)(2)(3)(4)(5)();          // 15

Why it works. Every invocation of sum creates a new scope with its own total variable. The next function closes over that total, so successive calls mutate the same binding. The closure is what makes "infinite" currying possible — there's no array, no global, just one captured variable per chain.

Approach 2: valueOf / toString coercion (no terminator).

You can avoid the trailing () by returning a function object whose valueOf returns the running total. When JavaScript needs to coerce the function to a primitive (e.g., + arithmetic, template literal interpolation, console.log), it calls valueOf.

ts
function sum(a: number): any {
  const next = (b: number) => sum(a + b);
  next.valueOf = () => a;
  return next;
}

sum(10)(20)(30) + 0;          // 60  — coerced via valueOf
\`\${sum(1)(2)(3)}\`;     // "6"

This is "slick but surprising" — it relies on implicit coercion, which TypeScript can't type cleanly, and the function's identity is hard to inspect in a debugger. Mention it as a senior flourish; don't use it in production.

Approach 3: fixed-arity currying.

If the question is "given add(a, b, c), write curry(add) so curry(add)(1)(2)(3) === 6," you can write a generic helper:

ts
function curry<T extends (...args: any[]) => any>(fn: T) {
  return function curried(...args: any[]): any {
    if (args.length >= fn.length) return fn(...args);
    return (...rest: any[]) => curried(...args, ...rest);
  };
}

const add = (a: number, b: number, c: number) => a + b + c;
curry(add)(1)(2)(3);      // 6
curry(add)(1, 2)(3);      // 6  — flexible argument grouping
curry(add)(1)(2, 3);      // 6

This relies on fn.length (the declared arity) as the termination condition. Doesn't work for variadic functions.

For mul(2)(3)(4) === 24 specifically (the file-4 phrasing), the answer is identical, swapping + for *:

ts
function mul(a: number) {
  return function next(b: number) {
    return mul(a * b);
  } as ((b: number) => any) & { valueOf: () => number };
}
// To make it terminate: add valueOf or use the empty-call pattern.

Talking points (the senior signal):

  • Closure — each chain has its own total. The function "remembers" because the engine keeps the environment record alive while next is reachable.
  • Function-as-first-class-value — you're returning functions from functions, an idiomatic FP move.
  • Memory — each call allocates a closure; not free for very long chains. If you needed billions of items, prefer array.reduce.
  • Type-ability — the recursive return type number | typeof next is awkward in TypeScript; the valueOf approach is even harder to type. Mention it as a downside.
  • Alternatives — if the API is sum(...nums), just use rest params. Currying is for partial application and lazy evaluation, not for show.
  • Debuggability — a chain of arrow-returning calls is hard to read in a stack trace; named inner functions help.

Common mistakes:

  • Forgetting to capture the running total in a closure (using a module-level variable — fails for concurrent chains).
  • Returning next() (calling it) instead of next (the function reference) at the recursive step.
  • Using arrow class fields with state that should be per-instance (works but allocates).
  • Not handling the empty-call termination case.

Interview-ready summary: "I'd return a function that's dual-purpose: with an argument it accumulates and returns itself, with no argument it returns the closed-over total. Each top-level call creates a fresh closure so chains are independent. The flashier valueOf variant skips the terminator but relies on implicit coercion, which I'd avoid in production."

Code

ts
function sum(a: number) {
  let total = a;
  function next(b?: number): any {
    if (b === undefined) return total;
    total += b;
    return next;
  }
  return next;
}

sum(10)(20)(30)();              // 60
sum(10)(20)(30)(40)(50)(60)();  // 210
Approach 1 — terminate with empty call
ts
function sum(a: number) {
  const fn: any = (b: number) => sum(a + b);
  fn.valueOf = () => a;
  return fn;
}

+sum(10)(20)(30);  // 60  — coerced via valueOf
Approach 2 — implicit terminate with valueOf

Follow-up questions

  • Implement a curry(fn, arity) that auto-curries any n-ary function.
  • Why doesn't sum(10)(20)(30) return 60 directly without a terminator?
  • What's the memory cost of a long curry chain?

Common mistakes

  • Forgetting the no-argument terminator and returning a function instead of the total.
  • Using `arguments` inside an arrow function — there is no `arguments`.
  • Not handling the first call's accumulation — initialising `total` outside `sum` (shared across calls).

Performance considerations

  • Each call creates a new closure frame. Fine for interview-scale; for hot paths, prefer accumulator patterns.

Edge cases

  • Calling `sum()` with no args first — guard with a default of 0.
  • Mixing the two approaches (terminator + valueOf) makes the API unpredictable. Pick one.

Real-world examples

  • Lodash's `_.curry` and Ramda's pervasive currying use the same closure-based technique.

Senior engineer discussion

Senior signal: discuss currying vs partial application, how it composes with point-free style, and why TypeScript's variadic tuple types make a typed curry possible (with caveats).

Related questions