Higher-Order Functions & Currying
Key Learning Objectives
Understand higher-order functions: functions that accept or return other functions.
Master currying and partial application using lexical closures.
Distinguish between function composition (right-to-left) and pipeline execution (left-to-right).
Apply pure functions without side effects to maintain deterministic application state.
The Interview Problem
What is logged to the console when the following code executes, and how does the execution order differ between pipe() and compose()?
1const add = (a) => (b) => a + b;2const multiply = (a) => (b) => a * b;34const addFive = add(5);5const double = multiply(2);67const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);8const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);910const pipeline = pipe(addFive, double);11const composed = compose(addFive, double);1213const res1 = add(3)(4);14const res2 = pipeline(10);15const res3 = composed(10);1617console.log(res1, res2, res3);
Predict Console Output
Select the option that matches what standard ECMAScript prints to the console:
7 30 25
7 25 25
7 30 30
12 25 30
V8 Engine Execution Trace
Step 1 of 9 (Line 1)Global Execution Context allocates curried function generators add and multiply in heap memory.
Deep Technical Breakdown
Higher-Order Functions, Currying & Composition
- First-Class & Higher-Order Functions: In JavaScript, functions are first-class citizens: they can be assigned to variables, passed as arguments, and returned from other functions. A Higher-Order Function (HOF) is any function that takes one or more functions as arguments or returns a function.
- Currying vs Partial Application:
- Currying: Transforming a function with arity $n$ into $n$ consecutive functions that each accept exactly one argument:
f(a, b, c) -> f(a)(b)(c). - Partial Application: Fixing a subset of arguments to produce a function of lower arity:
f(a, b, c) -> f(1)(b, c). - Both patterns rely on lexical closures to preserve outer argument bindings across invocations.
- Currying: Transforming a function with arity $n$ into $n$ consecutive functions that each accept exactly one argument:
- Function Composition (compose vs pipe):
- Pipe (
pipe(f, g, h)(x)): Left-to-right execution. Implemented withArray.prototype.reduce:x |> f |> g |> h->h(g(f(x))). - Compose (
compose(f, g, h)(x)): Right-to-left execution. Implemented withArray.prototype.reduceRight:f(g(h(x))).
- Pipe (
Common Traps & Mistakes
Reversing the execution order of `compose()` and `pipe()`. In interviews, `pipe` is left-to-right; `compose` is right-to-left.
Introducing side effects (like mutating an external array or global object) inside curried functions, destroying referential transparency.
Creating memory leaks by retaining large objects inside long-lived closures generated by currying.
FAANG Follow-Up Probes
Probe #1
How would you write a general auto-curry function in JavaScript that supports variable argument arity (e.g. curry(fn)(1, 2)(3) and curry(fn)(1)(2, 3))?
Probe #2
How does Redux apply compose() to middleware enhancers in applyMiddleware()?
Probe #3
What is Point-Free programming (tacit programming), and what are its trade-offs in frontend codebases?
