What are Pure Functions?

Terrick Mansur
5 min readDec 27, 2019

Programmer: I write Pure functions now.
Mathematician: You mean functions?
Programmer: No, PUREEEE functions!
Mathematician: What the heck is a PUREEEE function?

Exactly, what the heck is a Pure function? And why is this mathematician confused?

Let me explain.

A very common answer you will get when asking developers “what is a Pure function?” is that a pure function only does one thing. But this is incorrect. A Pure function is described as the following;

In computer programming, a pure function is a function that has the following properties:
- Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
- Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).

Plainly said, the function its output is only determined by its input, and it does not change any state that lives beyond its own scope.

So why was the Mathematician confused? And why was the Programmer so proud that he now write pure functions?

When you are writing a function in mathematics, it is always Pure. In mathematics there are no databases, no internet, no configuration files, no system files, no static variable. What is described by the function is always Pure by nature. It simply describes how to transform one set a data into another set of data. This is why the mathematician does not know what a Pure function is, all of their functions are Pure.

Here is a very, simple mathematics function;

square(x) = x * x

This statement defines a function square that takes x as a parameter, and return the square of x. Simple enough, I think we can all understand this. So let’s write this in code.

func square(x: Int) -> Int { 
return x * x
}

Again, simple enough, the function will return the square the parameter x.

So why are these Pure functions special? Well one of the big reasons for me, is testability and predictability. When a function, or a viewModel is Pure (whattttt, there are Pure viewModels? Kinda, but we will go into this in my next article) it becomes super easy to write unit tests for and very predictable by simply looking at the function name and parameters.

Here is a small example of a function that we would actually encounter in our day to day job.

func urlOfImageSize(imageName: String, size: Size) -> String {
return "\(Configuration.baseUrl)images/image=imageName?width=\(size.width)&height=size.height"
}

This function takes in an imageName and a Size that consists of, width and height, and returns a url String.

Let’s write the test for the this function.

XCTAssertEqual(
urlOfImageSize(imageName: "someimage", size: Size(width: 200, height: 400)),
"http://www.google.com/?imageName=someimage&width=200&height=400")
XCTAssertEqual(
urlOfImageSize(imageName: "someotherimage", size: Size(width: 400, height: 200)),
"http://www.google.com/?imageName=someotherimage&width=400&height=200")

The tests above simply checks if the function returns the URL we are expecting given the parameters. I wrote two so that we make sure the URL parameters are not hardcoded in the function.

So is this function Pure? At a quick glance, you might think that it is. Given the imageName, width,height we will always get the same URL. But this is incorrect. There is one more variable in this function determining the result that is not being passed in as a parameter, instead, it is just referenced smack in the middle of the function. The Configuration.baseUrl.

So without changing anything of the function itself, or the input, I can get these tests to fail. Simply change the Configuration.baseUrl.

struct Configuration {
static let baseUrl: String = “http://www.otherurl.com/"
}

Now both our tests are failing ( you could argue that the test should be written with the Configuration.baseUrl since the function its job is to add the parameters to the Configuration.baseUrl, but I would counter that by saying that if you are doing this, there is logic in your Unit-test and that this is not ideal. In any case, this article is about Pure functions, and if we make our functions Pure, we would no longer need to argue this). Given the input, this function does NOT always return the same result, this function returns the same result given the input AND Configuration.baseUrl . So let's re-write this function to be Pure.

func urlOfImageSize(
baseUrl: String, imageName: String, size: Size) -> String {
return "\(baseUrl)?imageName=\(imageName)&width=\(size.width)&height=\(size.height)"
}

Now the baseUrl is passed into the function as a parameter. We can say with 100% certainty, that given the function input, it will always return the same result, making it a Pure function. The new tests would look like this;

XCTAssertEqual(
urlOfImageSize(baseUrl: "http://google.com/",
imageName: "someimage",
size: Size(width: 200, height: 400)),|
"http://www.google.com/?imageName=someimage&width=200&height=400")
XCTAssertEqual(
urlOfImageSize(baseUrl: "http://www.doesnotmatter.com/",
imageName: "someotherimage",
size: Size(width: 400, height: 200)),
"http://www.doesnotmatter.com/?imageName=someotherimage&width=400&height=200")

These tests are as valuable as the other tests above. And won't fail if you change Configuration.baseUrl, and they shouldn’t, the function has nothing to do with the systems Configuration it was written to generate a URL given some parameters.

What is important to understand from Pure functions is the concepts of code being Pure. This concept can be applied on a bigger scale. Most codebases have impurity scattered everywhere (this is why the programmer was proud of his/her Pure Functions), this causes weird and unpredicted behavior that makes your code very difficult to follow, understand, and test.

I do realize I did not go into the second property of a Pure function; not having any side effects. But I wanted to highlight how easy testing becomes when you are working with Pure code and how clear it becomes what the function is doing when it is Pure.

The example above is a very simple one, you might not see the benefits of Pure code with this simple example yet. But in my next article, I will apply this concept of Pure function/code, on a bigger scale. We will be converting a viewModel that would be considered impure, into a pure viewModel. This will better show how making your code Pure makes for much easier unit testing.

If you like this article please follow me and show some love with a clap. If you want to read my other articles, below is a list of all my past articles.

All past articles:
1. The missing piece of MVVM

Get in touch:

--

--