Pure Functions
Introduction
A pure function always returns the same output for the same input and has no side effects. It does not mutate inputs, write to external state, read changing external state, perform I/O, or depend on time or randomness.
Purity is not a moral rule; it is an engineering tool. Pure functions are easier to test, cache, refactor, parallelize, and reason about.
Why This Matters
Pure functions appear in interviews through reducers, React state updates, memoization, testability, and referential transparency. They also reveal whether you can separate business logic from side effects like logging, network calls, and mutation.
Theory
The two rules
A function is pure if it is deterministic and side-effect free. Deterministic means the same inputs always produce the same output. Side-effect free means it does not change anything outside itself and does not depend on changing outside state.
Side effects include mutating an input object or array, mutating module/global state, logging, network calls, DOM updates, timers, file I/O, and directly reading changing values such as Date.now() or Math.random().
Referential transparency means a function call can be replaced by its returned value without changing program behavior. add(2, 3) can be replaced by 5. Date.now() cannot, because it changes over time.
Real applications need side effects. The practical architecture is to keep calculations pure and push side effects to the edges: parse input, call pure logic, then perform output. Pure functions often return new objects or arrays instead of mutating existing ones, which is why reducers in UI state management return next state without modifying previous state.
Visual Diagrams
impure input
read event, API, time
|
v
pure core
data in -> deterministic calculation -> data out
|
v
impure output
render, log, save, sendKeep side effects at the edges and the decision-making core pure.
Code Examples
Impure mutation vs pure update
The pure version returns a new array and leaves the original untouched.
Inject changing values instead of reading them
Passing the timestamp in makes the formatter deterministic and testable.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1const numbers = [1, 2];2 3function appendImpure(items, value) {4 items.push(value);5 return items;6}7 8function appendPure(items, value) {9 return items.concat([value]);10}11 12const a = appendImpure(numbers, 3);13const b = appendPure(numbers, 4);14 15console.log(numbers.join(','));16console.log(a === numbers);17console.log(b.join(','));18console.log(b === numbers);Coding Exercises
Update cart quantity purely
MediumImplement updateCartQuantity(cart, id, quantity) as a pure function. Return a new cart array. For the matching item, return a new object with the updated quantity. Non-matching items should keep their original object references. Do not mutate the input array or item objects.
Interview Questions
1What makes a function pure?
It is deterministic and side-effect free. Given the same inputs, it returns the same output every time, and it does not mutate inputs or external state, perform I/O, depend on time or randomness, or change anything outside itself. Pure functions are easier to test, memoize, and reason about.
Follow-ups
- Is `Math.random()` pure?
- How do pure reducers update nested state?
2What is referential transparency?
An expression is referentially transparent if it can be replaced by its value without changing program behavior. Pure function calls have this property: add(2, 3) can be replaced with 5. Calls such as Date.now() or functions that mutate state are not referentially transparent.
Quiz
1. Which function is pure?
Summary
- Pure functions are deterministic and side-effect free.
- They do not mutate inputs or depend on changing external state.
- Referential transparency means a call can be replaced by its returned value.
- A practical architecture keeps business logic pure and pushes side effects to the edges.
Cheat Sheet
Pure: same input -> same output, no side effects.
Avoid inside pure functions: mutation, globals, logging, DOM, network, timers, Date.now(), Math.random().
Benefits: easy tests, memoization, predictable reducers, safer refactors.
Pattern: pure core + impure shell.
Immutable update: return new arrays/objects; preserve references for unchanged pieces when useful.