Skip to content

JavaScript Tutorial Functional JavaScript

Function Composition and Currying

Lesson 205 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

Composition means building a bigger function by chaining small ones, so the output of one becomes the input of the next.

script.js
JS
const trim = s => s.trim();
const lower = s => s.toLowerCase();
const dashes = s => s.replaceAll(" ", "-");

// Without composition: nested calls read inside out
console.log(dashes(lower(trim("  Hello Big World  ")))); // → hello-big-world

pipe: left to right

A tiny helper makes the order read naturally.

script.js
JS
const pipe = (...fns) => input => fns.reduce((value, fn) => fn(value), input);

const slugify = pipe(
  s => s.trim(),
  s => s.toLowerCase(),
  s => s.replaceAll(" ", "-")
);

console.log(slugify("  Hello Big World  ")); // → hello-big-world
console.log(slugify("JavaScript Is Fun"));   // → javascript-is-fun

pipe(a, b, c)(x) means c(b(a(x))), but reads in the order things happen. Its mirror, compose, runs right to left (reduceRight).

Small, single-purpose functions

Composition works best when each function does one thing, and is pure.

script.js
JS
const pipe = (...fns) => input => fns.reduce((value, fn) => fn(value), input);

const orders = [
  { id: 1, total: 20, paid: true },
  { id: 2, total: 50, paid: false },
  { id: 3, total: 35, paid: true }
];

const paidOnly = list => list.filter(order => order.paid);
const totals = list => list.map(order => order.total);
const sum = list => list.reduce((a, b) => a + b, 0);

const paidRevenue = pipe(paidOnly, totals, sum);
console.log(paidRevenue(orders)); // → 55

Partial application

Fix some arguments of a function in advance and get a new, smaller function. This is how you adapt functions to fit a pipeline.

script.js
JS
const multiply = (a, b) => a * b;
const double = multiply.bind(null, 2);

console.log(double(7)); // → 14

const partial = (fn, ...preset) => (...rest) => fn(...preset, ...rest);
const triple = partial(multiply, 3);
console.log(triple(5)); // → 15

Currying

A curried function takes its arguments one at a time, returning a new function each time.

script.js
JS
const add = a => b => a + b;

console.log(add(2)(3));      // → 5
const addTen = add(10);
console.log(addTen(5));      // → 15

const greet = greeting => name => `${greeting}, ${name}!`;
console.log(["Maya", "Sam"].map(greet("Hello"))); // → ["Hello, Maya!", "Hello, Sam!"]

The last line shows why currying is useful: greet("Hello") produces the one-argument function that map needs.

Method chaining is composition too

script.js
JS
const result = [5, 3, 8, 1]
  .filter(n => n > 2)
  .map(n => n * 2)
  .sort((a, b) => a - b);

console.log(result); // → [6, 10, 16]

Array methods already form a pipeline: each step returns an array for the next.

Do not overdo it

Composition and currying are tools. A short pipeline of well-named functions is lovely. A chain of nested higher-order helpers that nobody can read is not. If a plain function with a few lines is clearer, write that.

info
Related
These ideas build on higher-order functions and closures.