Functional programming is not a religion — it is a set of constraints that make code easier to reason about, test, and compose. Pure functions with no side effects are trivially testable. Immutable data prevents spooky action at a distance. Function composition builds complex behavior from simple pieces. You can apply FP principles in any JavaScript codebase today without going full Haskell.
⚡ TL;DR: Pure function: same inputs → same output, no side effects. Immutability: never mutate, always create new. Higher-order functions: functions that take or return functions. Composition: f(g(x)) builds pipelines. Currying: convert f(a,b) into f(a)(b) for partial application.
Pure functions — the foundation
// Impure: depends on or modifies external state
let total = 0;
function addToTotal(amount) {
total += amount; // Mutates external state — impure!
return total; // Output depends on external state — impure!
}
// addToTotal(5) might return 5, 15, 100 — depends on external state
// Pure: same inputs → same output, zero side effects
function add(a, b) { return a + b; }
add(2, 3); // Always 5. Testable in one line. Composable.
// Benefits of pure functions:
// - Trivially testable: no mocks, no setup, just input → output
// - Cacheable (memoizable): same input → same output → cache it
// - Parallelizable: no shared state = no race conditions
// - Debuggable: output is fully determined by input
Immutability — never mutate
// Mutation causes bugs that are hard to trace
const user = { name: 'Alice', scores: [10, 20, 30] };
function addScore(user, score) {
user.scores.push(score); // MUTATION: modifies original!
return user;
}
// Caller's user is now modified — unexpected!
// Immutable: always create new
function addScore(user, score) {
return { ...user, scores: [...user.scores, score] };
}
// Original user unchanged. New object returned.
// Array operations:
const arr = [1, 2, 3];
// Mutable (avoid): push, pop, shift, unshift, splice, sort, reverse
// Immutable (use):
arr.map(x => x * 2) // New array
arr.filter(x => x > 1) // New array
[...arr, 4] // New array with 4 added
arr.slice(0, -1) // New array without last
[...arr].sort() // Sort copy, not original
Higher-order functions and composition
// Higher-order: functions that take/return functions
const double = x => x * 2;
const addOne = x => x + 1;
const isEven = x => x % 2 === 0;
// Compose: apply functions right to left
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
const transform = pipe(
x => x * 2, // 5 → 10
x => x + 1, // 10 → 11
x => x ** 2 // 11 → 121
);
transform(5); // 121
// Real pipeline:
const processUsers = pipe(
users => users.filter(u => u.active),
users => users.map(u => ({ ...u, name: u.name.trim() })),
users => users.sort((a, b) => a.name.localeCompare(b.name)),
users => users.slice(0, 10)
);
processUsers(rawUsers); // Pure pipeline, each step testable in isolation
Currying and partial application
// Currying: f(a, b, c) → f(a)(b)(c)
const curry = fn => {
return function curried(...args) {
if (args.length >= fn.length) return fn(...args);
return (...more) => curried(...args, ...more);
};
};
const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6
// Partial application: pre-fill some arguments
const multiply = (factor) => (x) => x * factor;
const double = multiply(2);
const triple = multiply(3);
[1, 2, 3].map(double); // [2, 4, 6]
[1, 2, 3].map(triple); // [3, 6, 9]
// Real use: customized utility functions
const filterBy = curry((field, value, arr) => arr.filter(item => item[field] === value));
const activeUsers = filterBy('status', 'active');
const premiumUsers = filterBy('tier', 'premium');
activeUsers(users); // Filter for active
premiumUsers(users); // Filter for premium
FP cheat sheet
- ✅ Pure functions: same input → same output, no side effects
- ✅ Immutability: spread operator, Object.freeze, Immer for complex state
- ✅ map/filter/reduce over imperative loops for data transformation
- ✅ Function composition with pipe() for readable data pipelines
- ✅ Currying for reusable utility functions with partial application
- ❌ FP is not “no classes ever” — use the right tool for each problem
- ❌ Don’t over-abstract simple code — readability > purity
Functional programming concepts power the generator function patterns — generators are lazy functional pipelines. The React performance guide builds on FP: pure components, immutable state updates. External reference: Mostly Adequate Guide to Functional Programming.
Recommended Reading
→ Designing Data-Intensive Applications — Essential for every senior developer. Distributed systems, databases, and production architecture.
→ The Pragmatic Programmer — Timeless engineering wisdom for writing better code at any level.
Affiliate links. We earn a small commission at no extra cost to you.
Free Weekly Newsletter
🚀 Don’t Miss the Next Cheat Code
Join 1,000+ senior developers getting expert-level JS, Python, AWS, system design and AI secrets every week. Zero fluff, pure signal.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
