mediumJS ES5#143

curry and compose

Prompt

Two functional-programming staples:

  1. curry(fn) — collects arguments across calls (f(1)(2), f(1, 2), f(1)(2, 3) all work) and invokes fn once it has fn.length of them
  2. compose(...fns) — right-to-left composition: compose(f, g)(x) === f(g(x))

Solution

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) return fn.apply(this, args)
    return (...more) => curried.apply(this, [...args, ...more])
  }
}

function compose(...fns) {
  return input => fns.reduceRight((acc, fn) => fn(acc), input)
}
Mentor's take

These two are asked together because they're the same idea from both ends: treating functions as data. Curry specializes a function by fixing arguments; compose builds a pipeline out of small functions. Every middleware chain you've used — Redux, Express — is compose wearing a costume.

The parts worth narrating:

  • fn.length is the arity — the number of declared parameters — and it's the termination condition. Senior caveat to say out loud: default parameters and rest args don't count toward .length, so curry((a, b = 1) => ...) has length 1 and fires early. Curry works on honest signatures.
  • Accumulate, never mutate: [...args, ...more] builds a fresh array per partial call, so const add2 = add(2) can be reused safely — a mutated shared array would make two partials contaminate each other.
  • reduceRight is right-to-left because composition reads like math: compose(f, g)(x) is f(g(x)). If the interviewer asks for pipe, it's the same body with reduce — say that instead of writing it twice.

The Redux connection that lands: applyMiddleware literally composes middlewares, and each middleware's store => next => action => ... signature is a curried function — you're writing this pattern every time you write a middleware, whether you name it or not.

Say it: "Curry accumulates arguments until fn.length is satisfied; compose is reduceRight over the functions — and Redux middleware is exactly these two patterns combined."