We call a function pure if it meets the following criteria
- It does not depend on hidden state, or equivalently it only depends on its inputs.
- Evaluation of the function does not cause side effects
In short the internal work of a pure function is isolated from the rest of the program.
This is made clear by two examples:
# A pure function def min(x, y): if x < y: return x else: return y # An impure function exponent = 2 def powers(L): for i in range(len(L)): L[i] = L[i]**exponent return L
min is pure. It always produces the same result given the
same inputs and it doesn’t affect any external variable.
powers is impure for two reasons. First, it depends on a
exponent, which can change [*]. Second, it changes the
L which may have external state. Consider the following execution:
>>> data = [1, 2, 3] >>> result = powers(data) >>> print result [1, 4, 9] >>> print data [1, 4, 9]
We see that
powers affected the variable
data. Users of our function
might be surprised by this. Usually we expect our inputs to be unchanged.
Another problem occurs when we run this code in a different context:
>>> data = [1, 2, 3] >>> result = powers(data) >>> print result [1, 8, 27]
When we give
powers the same inputs we receive different outputs; how could
this be? Someone must have changed the value of
exponent to be
producing cubes rather than squares. At first this flexibility may seem like a
feature and indeed in many cases it may be. The cost for this flexibility is
that we need to keep track of the
exponent variable separately whenever we
powers. As we use more functions these extra variables become a
|[*]||A function depending on a global value can be pure if the value never changes, i.e. is immutable.|
Impure functions are often more efficient but also require that the programmer “keep track” of the state of several variables. Keeping track of this state becomes increasingly difficult as programs grow in size. By eschewing state programmers are able to conceptually scale out to solve much larger problems. The loss of performance is often negligible compared to the freedom to trust that your functions work as expected on your inputs.
Maintaining state provides efficiency at the cost of surprises. Pure functions produce no surprises and so lighten the mental load of the programmer.
As an added bonus, testing pure functions is substantially simpler than testing impure ones. A programmer who has tried to test functions that include randomness will know this first-hand.