/ Programming

What is a Pure function? Explained simply with examples

Pure functions are deterministic

Pure functions only care about their input

They keep state local. Pure functions do not reference any external values. This means that pure functions will always produce the same output based upon the input. Pure functions are deterministic.

Not referencing anything but the input makes the function much easier to use, as it works in a vacuum, the function can be worked on and understood with no knowledge of the surrounding program. Purely looking at the function as a map of input to output.

A deterministic function

function addDeterministic(firstInput, secondInput) {
    return firstInput + secondInput;

A non-deterministic function

var secondInput = 1;
function addNonDeterministic(firstInput) {
    return firstInput + secondInput;

Deterministic functions are predictable. Relying only their input means that input is converted the same every time computation is performed.

Looking at our addNonDeterministic function, we see when we run our function

var resultOne = addOneNonDeterministic(1);

resultOne = 1, but what happens if secondInput changes

secondInput = 3;
var resultTwo = addOneNonDeterministic(1);

resultTwo = 4, but we gave exactly the same input!
This shows us that addOneNonDeterministic is indeed non-deterministic, and is therefore not a pure function.

addDeterministic only ever references its input, it is therefore deterministic. No matter when, or where we call addDeterministic it will consistently return the same value. Pure functions are repeatable.

This property of pure functions means their output can be cached. This is called memoisation [1]
addNonDeterministic(x,y) will always return the same value, so why do we need to compute the answer more than once!

Common examples external calls:

  • Getting a response from an API
  • Accessing a database
  • Referencing a value from a higher level of scope
  • Calling a non-deterministic function
  • Referencing time/date

Pure functions have no side effects

Pure functions return, they don't change

A pure function does not reference have side effects. This means that pure functions will not change anything outside the function.

Common side effects:

  • Setting a value outside the function
  • Logging to the console
  • Calling an API

A function with no side effects

var output = 1;
function add(firstInput, secondInput) {
    return firstInput + secondInput;

A function with side effects

var output = 1;
function addSideEffect(firstInput, secondInput) {
    output = firstInput + secondInput;
    return true;

Looking at our addSideEffect function, we see when we run our function

var resultOne = addSideEffect(1,2);

The function changes the output variable. The function is deterministic, but has a side effect and is therefore not a pure function.
Pure functions return the computed value, while this function directly changes another var. This would be the same if we updated a database or API with our result. A pure function does not change anything outside of it's scope.

Why care

We want programs to have side effects.

  • Send an email
  • Show data on the screen
  • Log to the console

We do want to minimise those side effects to only those we want to happen.

Pure functions are easier to read. A pure function maps an input to an output. Pure functions self document with their signature, using verbs as their names. Understanding what a function named add, double, or canShowLabel greatly alleviates the mental toll of figuring out what a function is intended to return.

Pure functions are easier to debug. Inputs and side effects are never hidden in the code. The function does not need to be looked at in the context of the rest of the program.

Pure functions are easier to test. Pure functions do not need to have a program around them to test. The function can be tested alone in a vacuum from anything else. Being deterministic means the function is reproducible. Having no side effects means testing is limited to checking the returned value of the function.

square = x => x**2;
// Will continue with no issues as square(3) == 9 is truthy
console.assert(square(3) == 9, "square(3) is not 9")
// Will cause an error as square(3) == 9 is falsy
console.assert(square(3) == 3, "square(3) is not 3")

Pure functions are self contained. They work in isolation, with no knowledge of anything but their input.

Pure functions don't care about order. Parallelisation is possible as each function does not share state with anything else, and therefore can be computed without care of what other functions are doing. You don't have to think about what happened before, during, or after. The output is solely dependent on the input, and therefore pure functions can be computed all at once.

var numbers = [1, 4, 9];
var squares = numbers.map(square);

Each number in numbers can be computed individually to map to a new array. Pure functions more likely to be thread safe as they do not reference any shared state [1:1].

Thanks to Andrew for helping with this blog

  1. Objects passed by reference can cause thread safety problems ↩︎ ↩︎