April 14, 2019

Just Enough FP: Pure Functions

edit

Another fundamental of functional programming is a solid understanding of pure functions. A pure function is one in which its output is derived solely from its inputs, with no side effects. I know that might still seem like gibberish to some of you, so I’ll break this down and hopefully make it simple and clear.

The First Part

The first phrase to break down and clarify is “its output is derived solely from its inputs”. Another way to say that is this: given the same arguments, the function always returns the same value. Let’s look at a basic example.

// A pure add function
function add(x, y) {
  return x + y
}

// or with an ES6 arrow function
const add = (x, y) => x + y

The add function will always return the same output given the same arguments. In fact, all functions in math are pure functions, so there’s a good chance you’re already familiar with the concept.

Now, what if the output doesn’t depend on the inputs? Is it still a pure function?

function subtractFromMaxSafeInteger(number) {
  return Number.MAX_SAFE_INTEGER - number
}

In the subtractFromMaxSafeInteger function, given the same input, we always get the same output, right? Sure looks pure.

But it isn’t.

Our output depends on a global constant Number.MAX_SAFE_INTEGER. This makes it impure, as our output should derive solely from the inputs. If our function was dropped into another program that redefined Number.MAX_SAFE_INTEGER, we’d end up with different outputs, which would break our function’s purity.

In other words, the “outside world”, that is the scope outside of our function, should have no affect on the output of our function.

We also need to be aware of any impure functions within the function we are evaluating as they will impurify an otherwise pure function. Let me show you an example of what I mean. I’ll create a factory function that makes “friend” objects for a program. I’ll want unique IDs for each “friend”, so I’ll create a generateID function as well.

// DISCLAIMER: Do not use this to create IDs. There are much better ways. Go find them.
const generateID = () => Math.floor(Math.random() * 10000)

const friendFactory = (name, age) => ({
  id: generateID(),
  name,
  age,
})

I could pass the same name and age to the friend factory, but I’ll get different results each time because generateID is an impure function.

friendFactory('Anna', 31) // {id: 6777, name: "Anna", age: 31}
friendFactory('Anna', 31) // {id: 6233, name: "Anna", age: 31}
friendFactory('Anna', 31) // {id: 7223, name: "Anna", age: 31}

Notice, that the id property is different in each object returned from our friendFactory function. We could make our friendFactory a pure function again by generating the ID outside of the function’s scope and passing it in as an argument instead.

The Second Part

Now, the second part of my description, “with no side effects”, is a little trickier to understand. The way I like to describe it is that a side effect is a change outside the scope of our function. Let me demonstrate with an example.

let count = 0

const addWithIncrement = (x, y) => {
  count++
  return x + y
}

addWithIncrement(1, 2)

This addWithIncrement function will always return the same value given the same inputs, and its output depends solely upon those inputs. However, every time we call this function, we change the value of count, which is outside the scope of our function.

Another surprising impure function that falls under this category is a function that has an effect on the world outside of our application. For example:

const log = x => {
  console.log(x)
}

log('This is an impure function. Wha???')

The log function doesn’t make a change inside of our program or application, but it affects the outside world, triggering the console to log out the value passed in. This is a side effect, and thus log is impure.

What Does This Mean?

It means that we need to be aware of which functions in our programs are pure and impure. We need impure functions for our applications to do anything meaningful. Strictly speaking, a program entirely made of pure functions (with no side effects) is equivalent to doing nothing at all.

But, using pure functions throughout our application until we need the side effect to do something meaningful will make our applications easier to test and reason about.

As we will see in future posts, pure functions create the foundation for some very cool functional programming techniques, especially composition.

In my opinion, your goal should be to use more pure functions in your application. Functions that do too much, or have random side effects, can result in buggy applications. Side effect bugs can be some of the most difficult to find and remove.


In this series, I’m going over material from my Just Enough Functional Programming in JavaScript course on egghead. This post is based on the lesson Identify Side Effects by Comparing Pure and Impure Functions.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Need help with your software problems?

My team and I are ready to help you. Hire Agathist to build your next great project or to improve one of your existing ones.

Get in touch
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Agathist
Good software by good people.
Visit https://agath.ist to learn more
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.
Logo for Just Enough Functional Programming
Just Enough Functional Programming
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.