Pure Functions Explained for Humans
Tags: programming, codequality, javascript
Published: 2021-12-21 16:00:52

## Start leveraging pure functions TODAY


First things first: you don't have to write code in a functional style to leverage pure functions.

This powerful tool makes it easier to read, reuse, maintain, and test code. Nobody wants to lose any of these benefits because their code is not functional. And you shouldn't neither. So get known to the concept now to make your code even better, functional or not.

 Good news: it is extremely easy to understand and start using pure functions.

## A simple definition
> A function can be called pure if it returns the same output given the same input every time you call it, doesn't consume or modify other resources internally, and doesn't change its inputs.

Ok, this seems to sound way easier than what we usually see when it comes to pure functions. Now let's break it down and see what each part of this definition means and how those parts are named in the professional lexicon.

## Returns the same output given the same input
This one means exactly what it says. Every time we call a function with a constant value, it has to return the same result. 

### Let's consider 2 examples
We will create `addFive` and `addSomething` functions and see how they follow(or don't follow) the rule. But before we move forward, can you guess which one violates the rule and why?

#### `addFive` function
```javascript
const seven = addFive(2); // returns 7
```

If we have an `addFive` function, we always expect that `addFive(2)` would return 7. No matter what happens with the rest of a program, when, or where in the code we call `addFive(2)`, it always gives us 7.

#### `addSomething` function
```javascript
const randomNumber = addSomething(2); // returns a random number
```

As opposed to `addFive`, there's the `addSomething` function. As we can guess from the name, it adds an unknown number to a passed value. So if `addSomething(2)` call returned 6, we have no guarantee that every time we call `addSomething(2)` it would return 6. Instead, this will be an arbitrary number that we can't predict at the moment of calling the function unless we know how the internal random number generator works. This function does not guarantee to return the same output given the same input.

### What does that mean for us?

At this point, we can definitely tell that `addSomething` is not a pure function. But we also cannot state that `addFive` is a pure function yet. To do this, we need to check if it satisfies other conditions.


## Doesn't consume or modify other resources internally
To explore this topic, we need to think about how the functions from the above examples would be implemented.

First, our pure function candidate, `addFive`:
```javascript
function addFive(number) {
  return number + 5;
}
```

As we can see, the function does exactly and only what it says and what we expect it to do. Nothing else other than adding 5 a passed number is happening. `addFive` passes this check. 

Now, let's define the `addSomething` function that is already known as impure:
```javascript
let callCounter = 0;

function addSomething(number) {
  callCounter = callCounter + 1;
  const isEvenCall = callCounter % 2 === 0;
  
  if (isEvenCall) {
    return number + 3;
  } else {
    return number + 4;
  }
} 
```

This function has an external variable that stores the number of times the function was called. Then, based on the counter, we check if it's an even call and add 3 if it is, or add 4 if it's not. This call counter is an external state that the `addSomething` function uses to calculate the results. Such states fall under the definition of side effects.

> Side effect is a modification of any external state, consumption of dynamic external values, or anything a function does outside of the work related to calculating the output.

In our case, `addSomething` modifies and uses `callCounter` to calculate the final output. This is a side effect. How could we fix `addSomething` to clean it up from side effects?

If we can't consume or modify an external variable, we need to make it an input:
```javascript
function addSomething(number, isEvenCall) {
  if (isEvenCall) {
    return number + 3;
  } else {
    return number + 4;
  }
} 
```

Now we control if it's an even or odd call from outside, and our `addSomething` function becomes pure. Whenever we call it with the same pair of inputs, it would return the same number.

Don't worry if you still don't quite understand what can be a side effect. We will see more examples of side effects a bit later.

## Doesn't change its inputs

For this part we need to create the `getFullName` function:
```javascript
function getFullName(user) {
  user.firstName = user.firstName[0].toUpperCase() + user.firstName.slice(1).toLowerCase();
  user.lastName = user.lastName[0].toUpperCase() + user.lastName.slice(1).toLowerCase();

  return user.firstName + ' ' + user.lastName;
}
```

The function takes an object with first and last names. Then it formats these properties in the object so they start with a capital letter and all other letters are lowercased. In the end, the function returns a full name.

If we skip over potential edge cases, our function will return the same output every time we pass an object with the same values. The function doesn't consume or modify any external resources neither and only calculates a full name. So, does that mean it's pure?

No. And here's why.

The object we pass to `getFullName` is a referential type. When we change its properties inside the function, the changes get reflected in the original object outside the function. In other words, we **mutate** our inputs.
```javascript
// note that all letters are lowercased
const user = {
  firstName: 'alex',
  lastName: 'khismatulin'
}; 

const fullName = getFullName(user); // returns "Alex Khismatulin"

// Logs "Alex Khismatulin", capitalized. Object is modified.
console.log(user.firstName + ' ' + user.lastName);
```

Even though primitive vs reference types separation sounds complex, in practice, it is not. Spend a few minutes to check it out. There are plenty of [good posts](https://www.google.com/search?q=primitive+types+vs+reference+types) on the topic. **Tip**: add your preferred language to the end of the search query to get more contextual results. Here's [an example for JavaScript](https://www.google.com/search?q=primitive+types+vs+reference+types+javascript).

Input mutations are also considered **side effects**. We change inputs that come from outside, so we're still changing an external resource but in a different way.

## "Same" doesn't always mean "equal"
As we just touched referential types, we should also note that even though pure functions always return the same output given the same inputs, this doesn't mean that all inputs and outputs must be **equal** to each other. That is possible when a function takes or returns a referential type. Look at this example:
```javascript
function createUser(firstName, lastName) {
  return {
    firstName: firstName,
    lastName: lastName,
  };
}
```

This function takes first and last names and creates a user object. Every time we pass the same names, we get an object with the same fields and values. But objects returned from different function calls are not equal to one another:
```javascript
const user1 = createUser('Alex', 'Khismatulin');
const user2 = createUser('Alex', 'Khismatulin');

console.log(user1.firstName === user2.firstName); // true
console.log(user1.lastName === user2.lastName); // true
console.log(user1 === user2); // false, objects are not equal
```

We see that `firstName` from `user1` is equal to `firstName` from `user2`. `lastName` from `user1` is equal to `lastName` from `user2`. But `user1` is not equal to `user2` because they are different object instances.

Even though the objects are not equal, our function is still pure. The same is applied to inputs: they don't have to be literally equal to produce the same output. It's just not a 100% correct word used in the first place.

### It's "identical", not "same" or "equal"
The word "identical" describes what we expect from pure functions best. Values such functions take or return don't necessarily have to be equal, but they have to be **identical**.

## Other side effects
So, what can be a side effect? Here are a few examples:
- Querying or changing external variables and states
- Mutating inputs
- DOM interaction
- Network calls
- Calling other impure functions

The list goes on and on, but you get the idea. **Anything unrelated to computing output or relies on any dynamic values other than inputs is a side effect.**

Moreover, `console.log` is also a side effect! It interacts with the console, thus doing work unrelated to computing an output. No worries, usually console logs have no impact, so this rule is omitted when debugging code.

## Final definition
Now, as we have all the pieces of our simple definition uncovered, we a ready to derive a smarter definition of a pure function:
> A function can be called pure if it returns identical output given identical input every time it is called and has no side effects.

Awesome! But there's one thing that might've been bugging you while reading.

## What should I do when I do need side effects?
Some things are impure by their nature. At the end of the day, this is what programming is about – transforming data is our bread and butter.

Side effects are imminent. But when we have to deal with them, we should strive to isolate them as much as possible and separate from the code that executes pure operations.

Here's a pretty widespread Redux selector pattern. We have a code that gets a snapshot of Redux state and a selector function that knows how to get a specific value from that state:
```javascript
function getUserById(state, userId) {
  const users = state.users.list || [];
  const user = users.find(user => user.id === userId);
  return user;
}

const state = store.getState();
const user = getUserById(state, '42');
```

> You don't need to know anything about Redux to understand the example. There's no magic going on here. `store.getState()` in our case only returns an object that holds some values.

In this example, the values in the store change dynamically and are out of our control. We secure the `getUserById` value selector function from any third-party states and make it only rely on its inputs.

You see the pattern: separate the code that has to deal with impure data sources or to produce side effects from the code that gives linear output based on its inputs.


## What are the pros?


### Reusability
Let's come back to the Redux selector example. Other than just returning a user from state, we can update the code and break it down into a few pure functions:
```javascript
function findUserById(list, userId) {
  const user = users.find(user => user.id === userId);
  return user;
}

function getUsersFromState(state) {
  const users = state.users.list || [];
  return users;
}
```

Now we have one function that knows how to get users from state and another one that knows how to find a user by id in a list of users. That means we can reuse `findUserById` in other parts of the app where we use the same shape for the user object:
```javascript
// find a user in the store users
const state = store.getState();
const users = getUsersFromState(state);
const user = findUserById(users, '42');

// find a user in the lottery players list
const lotteryPlayers = getLotteryPlayers();
const winnerId = (Math.random() * 100).toFixed();
const winner = findUserById(users, winnerId);
```

Both cases leverage `findUserById` because it does one small thing and has no unpredictable dependencies. If we ever needed to change the field name that holds user id, we would need to do that in just one place.

Purity gives us space to create functions that are not bound to specific data sources or context in which functions are called.


### Testing
We're going come back to the Redux selector example one more time and imagine that we decided to get state from the store right inside the selector function:
```javascript
function getUserById(userId) {
  const state = store.getState();
  const users = state.users.list || [];
  const user = users.find(user => user.id === userId);
  return user;
}

const user = getUserById('42');
```

What would it cost us to add a test that validates this function? Well, we would need to do some dark magic to mock `store.getState()`:

```javascript
test('Should return user with correct id', function() {
  store = {
    getState() {
      return {
        users: {
          list: [{ id: '42' }],
        },
      };
    }
  };

  const user = getUserById('42');
  expect(user.id).toBe('42');
});
```

You see what's going on? We had to mock the whole Redux store just to test one small selector. More importantly, the test must know **how** the state is retrieved from the store. Imagine what would we need to do to test a more complex one? What would happen if we decided to replace Redux with some other state management tool?

To see the difference, here's a test for the original pure version:
```javascript
test('Should return user with correct id', function() {
  const state = {
    users: {
      list: [{ id: '42' }],
    },
  };

  const user = getUserById(state, '42');
  expect(user.id).toBe('42');
});
```

Now we don't need to think about what method is used to return a state from the store and mock the whole thing. We just use a state fixture. If we ever change a state management tool, this will not affect the tests because they only know what the state's shape is, not how it's stored.


### They make the code easier to consume
Last but not least, writing pure functions forces us to create smaller, more specialized functions that do one small thing. The code is going to become more organized. This, in turn, will increase readability.


## In the end
Pure functions alone are not going to make your code perfect. But this is a must-have part of your toolset if you want to be a professional in what you do. Every little step moves you to a bigger goal, and pure functions are not an exception. Employ this concept and make your code a little better today.

I hope you learned something today. Make this topic a small piece in the strong foundation of your success. Thank you for reading!

## P.S.
If you like occasional no-bullshit web shorties, you should definitely drop me a line on [Twitter](https://twitter.com/alexkhismatulin). Feel free to tag me if you want to discuss this article, and I will make sure to join the conversation!