Summary
If you are in a hurry and just want the rough tl;dr of this article, here it is.
A pure function is simply one that returns the same input each time while an impure function depends on some external state.
Neither is better than the other, it really depends on the task at hand. I personally prefer pure functions because they are easier to reason with and easier to test.
I’ll be using python for most of this article so that you can run my code and follow along in an editor of your choice.
If you’re not sure what an editor or python is, just comment below and i’ll write up an article explaining that too if necessary.'
Functions
Before we begin to dive more into pure vs impure functions, Let’s start with a working definition of a function
A function is a process which takes some input and returns some output. We call its input(s) arguments and its output a return value.
A good example is the addition function which allows us to add two numbers
3 + 4 = 7
Note that we take in two arguments here 3 and 4. We then output a return value of 7 which we can derive by adding the two numbers together.
We can write this in python as seen below
def add(x,y):
return x + y
If you’re unfamiliar with the syntax here, a quick way to read it is
def <functionName>(argument 1, argument 2,…) indicates that we are defining a new function and calling it with the following arguments
return x+y indicates that we want to output the result of adding x and y together at the end of our function
We can then call it in the language itself by taking
add(4,5) # Returning 9
Impure Functions
An impure function is simply a function which does not return a consistent value given the same arguments
We can see an example of this when we look at the following function
def add(x,y):
if math.random() < 0.4:
return x + y
return x + y - 1
Using our previous example of
add(3,4) # Returns 6 or 7
This therefore shows that this is an impure function.
Pure Functions
A pure function is simply a function that returns the same values given a certain set of arguments.
Let’s see what this means by using the addition function we defined, for instance.
def add(x,y):
return x + y
Since add(3,4) will always return 5, it is a pure function since we can predict the result of the function given its arguments
Now that you’ve got the rough overview, let’s look at some of the more basic ideas behind a pure function.
A Set
If we had a collections of items named A which contained the numbers 3,4 and 5, we could denote it as
A = {3,4,5}
It’s important here to note that we could have written this in any order, for instance
A = {5,4,3}
A = {5,3,4}
or any of the other 6 permutations that `3, 4 and 5` can take. This brings us to a working definition of sets that we’ll use as
Definition 1 : A set is an unordered collection of sets with unique elements
This also means that if two sets have the same elements, we can essentially treat them as equal sets.
Maps
Let us now define two sets A and B where
A = {3,4,5}
B = {2,3,4}
We can define a function which allows us to take any value in A and derive a corresponding value in B. This function is known as a map since it maps values from A to B AND each value of B corresponds to a unique value in A .
Bringing it together
Thinking a bit more abstractly, we can think of a pure function as simply a map between two sets of values. In the example above, we saw
add(3,4) -> 7
which in more formal notation can be expressed as
(Z,Z) -> Z
where Z is simply the set of integers.
Pure Functions in Practice
More Numbers
At this point in time, it seems like there’s not much of a real difference between the two types of functions so let’s try to see an example of what it might look like in a more complex situation.
Let’s say instead of trying to add two numbers, now we want to perform multiplication. We have two solutions in front of us
The first is to simply write another function
def multiply(x,y):
return x*y
If we test this function, we’ll find that it works exactly as planned. However, it presents some problems. Namely that each time we want to support a new operation between two numbers, we need to write a new function. As we
Therefore, why not simply write a function that takes in other functions as an argument?
This could look something like this..
def higherOrderFunction(x,y,f):
return f(x,y)
def add(x,y):
return x+y
def mult(x,y):
return x*y
print(higherOrderFunction(6,7,add))
print(higherOrderFunction(4,5,mult))
#Fancier way using lambdas
print(higherOrderFunction(6,7, lambda x,y : x+y))
print(higherOrderFunction(6,7, lambda x,y : x*y))
This seems like perhaps a worser solution because
We have more code
We have to now pass in another argument which is the function itself
but this is largely because we are dealing with functions that only utilise a single line of code. When we move on to more complex functions that involve multiple steps and are significantly longer, that’s when pure functions really shine.
In these simpler cases, it’s often much easier to just write another functions
Conclusion
In this issue, we looked at a combination of pure and impure functions, mainly learning about the differences between the two. In the next issue, we’ll look at some other basic concepts around computer science before we move on to more complex data structures and algorithms.
Are impure functions a Python thing? I’m knee deep in JavaScript tutorials so seeing that was different.