What pure functions are, and why do more of them

What pure functions are, and why write more of them?

A pure function is a function that, given the same input, always returns the same output and does not have side effects.

Same result for same argumentsPermalink

The pure function will always return the same result for the same arguments. What exactly does it mean?

Let’s have a look at the following code.

other = 0
def add(a, b):
    return a + b

add(4, 5)
# outputs: 9

add(a, b) is a pure function because when we call it with arguments 4 and 5, it will always return 9.

We can call it now or tomorrow; we can call it once or thousand times; e call other functions before it or not; it will always return 9 if we give it 4 and 5.

We can change any other variables, but this function will always return 9 if we give it 4 and 5:

other = 10

def add(a, b):
    return a + b

add(4, 5)
# outputs: 9

The following function, however, is not pure:

other = 0
def no_pure_add(a, b):
    return a + b + other

no_pure_add(4, 5)
# outputs: 9

Why? Because if we change the variable other, our function will not return the same result if we give it the same arguments.

other = 2
no_pure_add(4, 5)
# outputs: 11

The above is a simple example, but it illustrates that function whose result can be affected by the outside state is not pure because its result is not predictable. This external state might be anything; a database that the function connects to, a file it reads, a global variable, time, or the use of another function which is not pure (usually it’s I/O).

No side effectsPermalink

A pure function has no side effects. That means it will not change (mutate) any variables outside of the function or change the state of anything else outside of the function.

It can’t have any other effect than its primary effect of returning the result.

Look at the following code:

def side_effect_add(a, b):
    result = a + b
    with open('result.txt', 'w') as result_file:
        result_file.write(str(result))
    return result

side_effect_add(4, 5)
# outputs:9

side_effect_add(a, b) is not pure function. It returns the same result every time given the same argument but it changes the outside state - a file system. So calling this function can affect other parts of the system. Other (un-pure) functions can rely on the state of the result.txt file and calling the side_effect_add might affect the results of other functions.

Why write more of themPermalink

Pure functions are:

  • easy to test
  • easy to reason about
  • easy to debug
  • and they make the whole system much more robust

Pure functions are straightforward to test. We call a function with parameters and check the result. We don't need to set up an external state; no need to do complicated mocks. It's a breeze to test them.

They're much easier to reason about because what you see is what you get. Since they're not dependent on the outside state, nor do they change the outside state, you can see everything that is going on within a function. When they use other functions, they are also pure, so we can just open that function and see what it is doing.

Debugging is also much easier because all we need to see is in our call stack and local variables. We don't need to check what is in the file system, database, other variables etc. It's all in front of our eyes.

These properties of pure functions make the whole system much more robust because you have code which has good tests, is easier to understand, and if there is a problem, you can debug it quickly. If the problem is in pure function is also fast to fix. And if the problem is not in pure functions, we'll find it out quickly, and we can focus on our un-pure functions.

Not all functions can be purePermalink

We can't have our programs written with pure functions only. Most programs need to use external resources, such as connection, display, database, etc.

The trick is to have as many pure functions as possible so we can easily test, reason about and debug most of the program and isolate code that deals with the external state (I/O) in a few places so that when there is a problem, we know where to look for it.

You might also like

Join the newsletter

Subscribe to get new articles about Python, code and programming into your inbox!