What is pure function? According to Wikipedia:
- The function always evaluates the same result value given the same argument value(s). (…)
- Evaluation of the result does not cause any semantically observable side effect or output (…).
The first point seems to be clear. It’s a ‘natural’ definition of a mathematical function we learn in the primary school. When can a function yield a different result given the same arguments? Only when it calls impure functions or depends on the external state in the calculations.
The second point mentions semantically observable side effect or output. First of all, what does semantically observed stand for?
Semantics is a branch of science that deals with meaning – a connection between a symbol and something in our senses or mind that the symbol expresses. We associate the word bird
with a flying animal covered by feathers. In a Java program, we know that the +
symbol stands for addition of numbers or concatenation of strings. Note that this is not the same algebraic operation; numeric addition is commutative, string concatenation is not.
So, for me, semantically observed means (pun intended) being significant in our program.
What is then a side effect? When we modify a global variable, we cause a side effect. Same with calling a setter on an object passed to us. Writing to a file is usually considered a side effect too. Global variables, all living objects during the execution of a program, current content of the filesystem – it’s all state. Side effects are changes to the external state from the perspective of a function that will stay visible after the function is executed.
Let’s see some examples (in ES6). It this function pure?
array => { array.reverse(); // more transformations of the array }
It uses the Array.reverse method that works in-place. Array object passed to the function is going to be modified – we are changing the external state of the function. Final verdict – we are dealing with an impure function.
Another, slightly modified version of this function:
array => { const clone = a => a.slice(0); const reversed = clone(array).reverse(); // more transformations of the array }
It makes a (shallow) copy of the array
argument before mutating it. Assuming that subsequent array transformations are pure, i.e. they don’t mutate the array elements, this function can be considered pure. Seen from the outside, there is no modification of state. array
, the only element of the external state known to our function, remains unchanged.
Conclusion
If you want to be pure, don’t get your hands dirty with the outside world:
- don’t rely on it in your calculations (point 1)
- don’t try to change it (point 2).
Additional reading
- StackExchange, What exactly does “semantically observable” side-effect mean?
- Tom Stuart, ch.2 “The Meaning of Programs”, Understanding Computation