If I could only give one piece of advice to the programmers I work with, it would be this:

“Use pure functions wherever you can – use them almost exclusively.”

What is a pure function anyway?

A pure function is a function in the mathematical sense. That is, a mapping from input to output. A pure function doesn’t actually do anything observable – it doesn’t talk to any APIs or update the DOM – it just does some computation and returns a value.

Pure functions are the bread and butter of functional programming, and they have two very important properties compared to “normal functions”:

Given the same input arguments the function always returns the same value. Another way of saying this is that the function does not depend on any information other than what is passed in as arguments.

Evaluating the function produces no observable side effects, such as changing state or I/O.

Given those properties, is the following function pure or not?

function getAge(born) { const now = new Date(); const age = now.getFullYear() - born.getFullYear(); const mDiff = now.getMonth() - born.getMonth(); if (mDiff < 0 || (mDiff === 0 && now.getDate() < born.getDate())) { return age - 1; } return age; }

Well it doesn’t do any I/O or mutatation, so it’s ok on that front. But does it always return the same value for the same input? Let’s try it out and see:

const birthday = new Date(1988, 5, 5); getAge(birthday); // returns 27 getAge(birthday); // returns 27

Seems to return the same value every time, so it looks good. It’s pure right? Well hang on, what if we hopped in a time machine and travelled to this time next year?

getAge(birthday); // returns 28

Hmm, same input as before but a different output. That means this function is not pure. The reason it’s not pure is because it depends on some mutable information we didn’t pass in: the current date.

Making this function pure is pretty easy, we just need to accept the current date as a parameter:

function getAge(now, born) { const age = now.getFullYear() - born.getFullYear(); const mDiff = now.getMonth() - born.getMonth(); if (mDiff < 0 || (mDiff === 0 && now.getDate() < born.getDate())) { return age - 1; } return age; }

Now it doesn’t matter when we call this function. As long as we call it with the same input it will always return the same value.

const now = new Date(2015, 5, 22); const birthday = new Date(1988, 5, 5); getAge(now, birthday); // returns 27, today or in 10 years time.

Why purity?

So why bother with purity? Why is it worth it to pass that extra parameter?

Simplicity. Pure functions are simple: they just take some arguments and return a value. This sounds like a trivial benefit, but hidden dependencies and side-effects are a nightmare. Our applications turn into balls of yarn precisely because random functions modify state or touch some other part of the system the caller can’t see. Pure functions can’t change state, they can’t trigger any events, and they can’t “launch the nukes”. They can only return values.

Fewer bugs. Although no code is completely bug-free, using pure functions avoids an entire class of bugs. You can call a pure function as many times as you need and you can be sure there aren’t any subtle interactions with other parts of the system, there aren’t any race conditions, and your system won’t end up in some invalid state.

Code re-use. Because pure functions only return a value this means they’re trivial to re-use. You don’t need to tease the logic apart from your persistance layer, add some flag to switch behaviour, or try to prevent some event being triggered, you just call the function and do something with the result.

Easier to test. Testing pure functions is simple. There’s no need to create complex stubs, fake environments, mock collaborators, or anything else like that. Just pass the input and check the output.

Improved performance potential. Pure functions always return the same output for a given input. This allows us to use memoisation, a powerful optimisation where the results for slow computations are cached to speed up subsequent calls. Further, pure functions are inherently thread-safe because all their data is either scoped to the function or immutable. You can take advantage of multiple cores without needing to worry about the complexities of concurrency. That said, pure functions are usually less efficient with CPU and memory resources since they copy immutable data structures rather than using direct mutation. However I wouldn’t normally consider that a big concern unless your application is extremely performance sensitive and you can’t take advantage of parellelism.

More fun. The less time I spend scratching my head in the debugger and the more time I spend creating value, the happier I am. Pure functions allow me to reason about programs more efficiently so I can spend more time doing the fun stuff.

There’s much more to functional programming than pure functions, but if you want to create reliable systems then pure functions are the foundation you should build upon.