Pure functions, and why I like them.
Tags: functional, javascript, purefunctions
Published: 2017-09-17 16:31:53

---
title: Pure functions, and why I like them.
published: true
description: A very basic look at pure functions, and why I think they make life better.
tags: functional, javascript, pure functions
---

Pure functions are not new. This isn't a new concept by any means at all, and this certainly isn't the first post anyone has written about them. But the benefits of pure functions are worth re-stating loud and often, because they make your life better. They're self-contained, they reduce cognitive load, increase testability, lead to fewer bugs, and are inherently reusable.

Before reading on, take a moment to consider what the following functions have in common.

```
const isOverLimit = x => x > limit
const multiply = x => x * config.requiredMultiple
const getItem = index => store[index]
const spaceAvailable = date => schedule[date].attendees < limitPerDay
```

#Predictability

None of the example functions are complicated by any stretch, but one thing these examples have in common is that you can't look at them and know what their return value will be. You can see that `isOverLimit` will return true or false, and you can infer that the point of that function is to find out if a supplied value is over a limit imposed by your system, but do you know whether it will return `true` if you call it with `isOverLimit(9000)`? You'd have to find out what `limit` was pointing to for this, increasing your cognitive load unnecessarily, and making you _look elsewhere in your codebase to understand the thing you were originally looking at_; too much of that leads to distraction and frustration in equal measure, in my experience at least.

Consider this alternative: 
```
const isOverLimit = (x, limit = 100) => x > limit
```

Now you can look at that function and see exactly what it'll return under any circumstance. You can see that `isOverLimit(9000)` will be `true`, and `isOverLimit(9000, 9001)` will be `false`. 


#Re-usability

Think again about my original `isOverLimit` function. Imagine that my Product Owner comes to me one day and says that our company is adding a new "Gold" membership level to our product, with its own special limit of `1000`. 
In my original code, perhaps I'd have `const isOverGoldLimit = x => x > goldLimit`, and I'd maintain `limit` and `goldLimit` somewhere. I'd just keep writing this same function for every new membership level introduced, right? 

But now that my `isOverLimit` is pure, I can just re-use it: 
```
const isOverGoldLimit = x => isOverLimit(x, 1000)
```

#Testability

So the example `multiply` function is working nicely in my imaginary system, which due to strict business requirements has to multiply things that we give it by a number that is set through a user's configuration, and can be updated at any time. Thanks to another business requirement, I'm not allowed to know what that number is. And thanks to a third business requirement, I must make sure I have an automated test that proves this function is working correctly. How do I do that? It doesn't take much to realise the answer is either "I can't", or if you're being generous, "with difficulty". But if I re-write it to be a pure function like I did with `isOverLimit`, it would look like this: 

```
const multiply = (x, y = config.requiredMultiple) => x * y
```

So, `config.requiredMultiple` can still be whatever it was before, but crucially I can easily write a test that checks that my function is working: `assert.equals(multiply(2, 4), 8)`

#No side effects

Pure functions can not cause anything to happen to any values outside of the function themselves. Consider the difference between `array.push` and `array.concat` in JS:

```
const updateItemsViewed = item => itemsViewed.push(item)
```

Great, this allows me to record what items have been viewed. But thanks to the side effect I've introduced here, this function doesn't give me the same output every time it's called with the same input. For example: 

```
let itemsViewed = ['item1', 'item2', item3']
console.log(updateItemsViewed('item4')) // ['item1', 'item2', 'item3', 'item4']
console.log(updateItemsViewed('item4')) // ['item1', 'item2', 'item3', 'item4', 'item4']
```

Consider again the automated test for this function - the complication you should see immediately is that the test itself will alter my `itemsViewed`, so when I run it a second time, it will add my `test` item a second time. You've probably seen this before, where automated tests have a "setup" or "teardown" to deal with "resetting" any side effects the tests themselves have introduced. But if your function was pure in the first place, you wouldn't have this issue: 

```
const itemsViewed = ['item1, 'item2', 'item3']
const updateItemsViewed = (item, itemsViewed = []) => itemsViewed.concat(item)
console.log(updateItemsViewed('item4', itemsViewed)) // ['item1', 'item2', 'item3', 'item4']
console.log(updateItemsViewed('item4', itemsViewed)) // ['item1', 'item2', 'item3', 'item4']

assert.deepEqual(updateItemsViewed('testItem'), ['testItem'])
```

Obviously the examples in this post are contrived to demonstrate the points I'm making, and of course you can't have a codebase entirely full of pure functions, unless the software you're writing is there to do nothing. But seriously, favour pure functions *everywhere you can*, and keep all your application's side effects to the "edges", and you'll thank yourself in the future. As will anyone else who has to look at your code. :) 


#TL;DR

Side effects are best avoided wherever they can be, and if you're strict about using pure functions you'll benefit from a codebase that is much easier to test, much easier to reason about, and much easier to extend and maintain. If your functions can be called without using their return value, then they're either not pure, or they're not doing anything. Either way, you can't re-use them or write tests for them (easily), and I'd strongly suggest you should consider changing them if they're anywhere but the very "edges" of your codebase.