Some functions behave like a spreadsheet formula: give them the same inputs and they always hand back the same output, while nothing else in the program moves.
Those functions are called pure. They can be tested without setup, read without context, and moved from one file to another without surprises.
Definition
A pure function satisfies two conditions at once. It always returns the same result for the same arguments, and it produces no Side effect, meaning no change outside itself.
function grossTotal(net, rate) {
return net * (1 + rate);
}
console.log(grossTotal(100, 0.2)); // 120
console.log(grossTotal(100, 0.2)); // 120, always
let discount = 0.1;
function impureTotal(net) {
return net * (1 - discount); // depends on an outside variable
}
console.log(impureTotal(100)); // 90
discount = 0.5;
console.log(impureTotal(100)); // 50, same call, different resultThe second function is not wrong, it is simply unpredictable in isolation: its result depends on a value that appears nowhere in its call.
The second trap: modifying its arguments
A function can return the right value and still be impure, because it changed what it was handed along the way.
function addImpure(list, item) {
list.push(item); // modifies the argument it received
return list;
}
function addPure(list, item) {
return [...list, item];
}
const cart = ["keyboard"];
addPure(cart, "mouse");
console.log(cart.length); // 1, the original is intact
addImpure(cart, "mouse");
console.log(cart.length); // 2The pure version produces a new list with Spread (...). That is the principle of Immutability, applied at the scale of a single function.
The impurities that hide
Math.random()andDate.now(): two identical calls, two results. Pass the value as an argument.- Reading an outside variable: the result depends on state invisible from the call.
- Modifying a received object: the caller ends up with data it never changed.
- Writing to a log or to storage: even without changing the result, it is an effect.
A pure function is memoizable by construction: since the result depends only on the arguments, the cache cannot be wrong. See Memoization.
Frequently asked questions
Can a whole program be pure?
No, and that is not the goal. A program that writes nothing, displays nothing and calls no service is useless. The aim is to gather the effects at the edge of the code and keep everything that computes pure, which is often most of an application.
Can a pure function call another function?
Yes, provided that one is pure as well. Purity propagates: a function that only calls pure functions and touches nothing outside stays pure. A single impure call in the chain, however, is enough to contaminate the whole thing.
What is the concrete benefit for tests?
A test on a pure function fits on one line: call it with values, compare the return. No database to prepare, no service to fake, no cleanup afterward. That is why a Unit test on impure code is almost always longer to write than to make pass.