Curry IILoading saved progress…

Curry II

Implement a more permissive variant of currying. Classic curry forces one argument per call: curry(fn)(a)(b)(c). This version is flexible — the caller can pass any number of arguments at each step, and the curried function keeps collecting until it has enough to invoke the original. So curry(fn)(a, b)(c), curry(fn)(a)(b, c), and curry(fn)(a, b, c) all produce the same result.

The "arity" of a function is fn.length — the count of named parameters in its declaration. Your job is to keep returning a new function that accumulates arguments until you have at least fn.length of them, then call fn with the collected args.

Signature

// Returns a curried wrapper that gathers args across any number of calls
// until fn.length args have been collected, then invokes fn(...args).
function curry(fn: (...args: any[]) => any): (...args: any[]) => any;

Examples

const sum3 = (a, b, c) => a + b + c;
const curried = curry(sum3);

curried(1)(2)(3);     // 6   — one arg at a time
curried(1, 2, 3);     // 6   — all at once
curried(1, 2)(3);     // 6   — 2 then 1
curried(1)(2, 3);     // 6   — 1 then 2
const greet = (greeting, name) => `${greeting}, ${name}!`;
const c = curry(greet);

c('Hello')('world');   // "Hello, world!"
c('Hi', 'Ada');        // "Hi, Ada!"

// fn.length === 0 — there's nothing to collect, so the curried form
// just calls fn() on first invocation.
curry(() => 42)();     // 42

Notes

  • Use fn.length for the arity. That's the count of named parameters before any default values or rest params. Don't try to be cleverer than fn.length.
  • Do not invoke fn early. As long as the total collected arguments is less than fn.length, return another curried function. Only when the count reaches fn.length (or more) do you call fn.
  • Extra arguments are passed through. If the caller supplies more arguments than fn.length (e.g. curried(1, 2, 3, 4) for a 3-arg fn), forward all of them to fn. The spec is "call once you have enough", not "trim to exactly fn.length".
  • Preserve this. When invoked with .call(ctx, ...) or .apply(ctx, ...), the original fn must receive that same this. A regular function (not an arrow) for the returned wrapper handles this naturally.
  • Zero-arity is a real case. curry(() => 42) must return a function that, when called with no args, returns 42.
  • Don't mutate shared state across calls. Two independent calls on the same curried function must not interfere — curried(1)(2) must not affect a sibling curried(10)(20).
Loading editor…