JavaScript Currying and Composition

Transform multi-argument functions into chained calls and combine small functions into pipelines.

TL;DR

  1. 01Transform multi-argument functions into nested single-argument curry functions.
  2. 02Apply subset arguments up front to generate specialized function presets.
  3. 03Compose independent single-input operations into clean linear data pipelines.

Tips

  1. 01Design small, single-purpose functions to make currying and function composition patterns easier to build.
  2. 02Prefer the pipe helper to construct left-to-right processing streams matching natural reading orders.

Warnings

  1. 01Avoid currying every simple method because unnecessary function wrappers hurt overall script readability.
  2. 02Validate data parameters at each pipeline step to prevent silent runtime type errors.

What Currying Does

    Currying chains

    Converts multi-argument functions into sequential single-argument calls.

    function add(a, b, c) {
      return a + b + c;
    }
    const curryAdd = a => b => c => a + b + c;
    curryAdd(1)(2)(3); // 6
    Call execution

    Invokes the original function only after all arguments are received.

    const add5 = curryAdd(5);
    add5(2)(3); // 10
    Arrow notation

    Uses nested arrow functions to construct inline curried definitions.

    const multiply = a => b => a * b;

A Generic Curry Helper

    curry() implementation

    Gathers arguments recursively until the count matches function arity.

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

    Resolves function arity dynamically using the fn.length property.

    const sum = curry((a, b, c) => a + b + c);
    sum(1)(2)(3); // 6
    sum(1, 2)(3); // 6
    Arity configurations

    Specifies manual arity limits for functions with default parameters.

    function curryN(fn, arity) {
      return function collect(...args) {
        return args.length >= arity
          ? fn(...args)
          : (...more) => collect(...args, ...more);
      };
    }

Partial Application

    bind() method

    Binds arguments up front using the built-in prototype bind method.

    function mult(a, b) { return a * b; }
    const double = mult.bind(null, 2);
    double(5); // 10
    Custom partial

    Builds a partial helper to bind arguments without bind context.

    function partial(fn, ...fixed) {
      return (...rest) => fn(...fixed, ...rest);
    }
    const greet = partial((g, n) => g + n, "Hi ");
    greet("Ada"); // "Hi Ada!"

Composing Functions

    compose() helper

    Combines functions executing right-to-left like mathematical equations.

    const compose = (...fns) => x =>
      fns.reduceRight((acc, f) => f(acc), x);
    const shout = s => s.toUpperCase() + "!";
    const format = compose(shout, s => s.trim());
    format("  hi  "); // "HI!"
    pipe() helper

    Combines functions executing left-to-right to match reading order.

    const pipe = (...fns) => x =>
      fns.reduce((acc, f) => f(acc), x);
    const run = pipe(s => s.trim(), s => s.toUpperCase());
    run("  hi  "); // "HI"

Practical Use Cases

    Curried validation

    Pre-fills validation parameters to generate specialized rule checklists.

    const minLength = curry((min, s) => s.length >= min);
    const isValid = minLength(3);
    isValid("ok"); // false
    Middleware execution

    Threads orders or values through sequential modifier function lists.

    const processOrder = pipe(
      applyDiscount,
      addTax
    );
    processOrder(100);

In Practice

FAQ