Why is determining if a function is pure difficult?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
A pure function is supposed to return the same result for the same input and produce no observable side effects. That sounds simple, but determining purity in real programs is difficult because side effects are often indirect, hidden behind abstractions, or dependent on context outside the function signature.
Purity Is About More Than Not Writing Globals
People often begin with an oversimplified rule such as "a pure function does not modify global state." That is part of the story, but it is not enough.
A function also stops being pure if it depends on anything outside its explicit inputs that can change between calls. That includes:
- current time
- random number generators
- environment variables
- database state
- network responses
- mutable objects passed by reference
So purity is about both outputs and dependencies. The function must be deterministic and isolated from external effects.
Hidden Inputs Make Analysis Hard
A function can look pure at the call site while still depending on hidden state internally.
The function takes one argument and returns a string, but it is not pure because the current time is an implicit input.
The same problem appears with randomness:
Even though nothing obvious is mutated, the result is not determined only by x.
Side Effects Are Not Always Obvious
Some side effects are easy to spot, such as writing to a file. Others are harder because they occur through mutable objects or indirect calls.
This function changes its argument in place, so it is impure even though the mutation happens through a parameter rather than through a global variable.
The difficulty increases in larger systems where a function calls another function that calls another function. To prove purity, you often need to understand the entire dependency chain, not just the top-level function body.
Language and Runtime Features Complicate the Question
Purity is easier to reason about in languages or code styles that encourage immutability and explicit effects. In imperative languages, aliasing and shared mutable state make that much harder.
For example, if a function receives an object reference, you may need to know:
- whether the object is mutable
- whether another part of the program can change it concurrently
- whether any method call on it performs I/O or mutation internally
At that point, purity analysis becomes a whole-program reasoning problem rather than a local syntax check.
Observation Depends on the Boundary You Care About
Another reason purity is hard is that "no side effects" depends on what counts as observable.
A function that memoizes results in an internal cache may look impure because it mutates internal state. But some discussions treat that mutation as unobservable if callers cannot detect it except through performance.
That means purity is sometimes partly definitional. Are we asking for strict mathematical purity, practical referential transparency, or no user-visible side effects. Different teams use those terms with slightly different boundaries.
Static Analysis Helps, But Only So Far
Linters, type systems, and effect systems can help identify suspicious operations, but most mainstream languages cannot prove purity in the general case. They may catch obvious file writes or mutable global access, yet hidden state, foreign function calls, reflection, and aliasing still limit certainty.
In other words, purity is often something you design for rather than something you perfectly infer afterward.
Common Pitfalls
A common mistake is assuming that a function is pure because it has no explicit print, file write, or global assignment. Hidden reads such as time, randomness, and external state still break purity.
Another mistake is ignoring mutation through parameters. Changing an input object is still a side effect.
People also often forget transitive dependencies. If a function calls another impure function, the caller is impure too.
Finally, teams sometimes use "pure enough" informally without agreeing on the boundary. That can cause confusion in API design and testing discussions.
Summary
- Purity requires deterministic output and no observable side effects.
- Hidden dependencies such as time, randomness, and external state make purity hard to judge.
- Mutation through parameters is still impurity, even without global variables.
- Real purity analysis often requires reasoning about the whole call chain.
- In practice, purity is easier to guarantee through design choices than to prove after the fact.

