Pure and Impure Functions

Pure and Impure Functions

Pure and impure functions are two common terms you will often come across in functional programming. These are fairly simple to understand but can get tricking sometimes.

In this article, you will learn what are side effects, what are pure and impure functions, and their advantages.

What are Pure functions?

Functions whose output strictly depends on the input passed and have no side effects whatsoever are called pure functions.

Let’s take the following function:

function add(num1, num2){
  return num1 + num2;
}
console.log(add(2,2)) // 4

The above function is pure because it has no side effects and given the same input, our add() function will always return the same output. This means that, even if you call add(2,2) a thousand times, it will always produce output as 4.

What are Impure functions?

Unlike pure functions, impure functions may have side effects. You can never be sure how the function will behave every time even if you called the function with the same parameters.

Let’s take the following function:

let addMe = 0
function impureAdd(num){
  return addMe+=num
}

console.log(impureAdd(10)) // 10
console.log(impureAdd(10)) // 20

Here, even though we are calling impureAdd() with the same parameter twice, it gives different output each time. This is because the function is not pure and has side effects.

But what are the side effects?

Side effects occur when your function uses external code, resulting in your function behaving differently every time.

In short, mutation of mutable objects or output to I/O devices causes side effects.

What do I mean by that?

Let's take the above example of impureAdd(),

let addMe = 0
function impureAdd(num){
  return addMe+=num
}

In this example, the function impureAdd() uses an external variable addMe. This gives rise to multiple side effects like:

  1. Dependency on external variables: Every time you call the impureAdd() function, the result will depend on the external addMe variable. What if the external variable no longer exists or has the value undefined? Your function will throw an error. This brings us to the second side effect.
  2. Function becomes non-deterministic: Since this function uses an external variable, you can never determine the output by just looking at the function. You will have to consider external factors i.e. the value of addMe variable.
  3. Modifies external code: Whenever you call impureAdd() function, the value of external variable changes (gets mutated).

Now that you have a clear idea of side effects and what pure and impure functions are, can you answer the below question?

Is console.log() a pure function?

Think about it…

Look at the code snippet and think...

let x = console.log('Apurva'); //  Apurva
console.log(x); // undefined
  • The first line will print "Apurva".
  • The output of console.log() is always undefined. Hence the second line will print undefined.

Since console.log() always returns undefined, it is a pure function.

Okay, So what if we use console.log() inside another pure function? Will the function remain pure?

Now, the answer is: No

console.log() causes a function to have side effects. This is because console.log() affects the external code i.e. the console object’s state. In other words, console.log() asks the computer to alter the state of the console object.

So, when you write a console.log() inside a function, it causes the following side effects:

  • The function becomes dependent on the console object to perform the task.
  • The function modifies the external state i.e. console object’s state.
  • The function becomes non-deterministic, as you now have to consider the console object’s state to be sure of your function’s output

Advantages of Pure functions:

  1. Avoids side effects: Pure functions do not modify any global variables or states. Hence, there are no side effects. And lesser the side effects, the better the readability, and code structure
  2. Memoization: Pure functions always return the same output given certain inputs. This means that the output can be cached when the function first runs. And next time you can use the cached value instead of running the function again. This will increase the performance.
  3. Easy to test and debug: Output of pure function is always deterministic. Since you can always predict the output, you need not worry about the function’s behavior and can debug and write test cases easily.
  4. Cloning is possible: You can clone an external state into pure a function. It won’t make the function impure since you are only creating a copy of the state which leaves no strings attached to the original state.

Example:

const myArray = ["Apurva", "Sawant"]
function updateArray (newValue, myArray){
  return [...myArray,  newValue]
}
console.log(updateArray("India" ,myArray)) //["Apurva", "Sawant", "India"]

Advantages of Impure functions:

Impure functions can be useful in certain contexts.

  1. We can use an in-place solution to reduce the space complexity. For example: sorting the array in-place
    [1,2,3,4,5].sort((a,b) => b-a)
    
  2. The state can be modified to use the parent variable and call for the function compiling.

Conclusion

Overall, pure functions avoid unnecessary issues in the code. But sometimes we might have to use impure functions. In that case, we should be mindful of the side effects caused by the function and try to minimize the code that might lead to unnecessary issues.

In interviews, you may be asked more tricky questions about it. All you have to do is check if the function has any side effects. If not, then the function is pure.

I hope you found this article useful. Thank you for reading.