Pure functions allow us to get a predictable result every time they are been invoked. They find its applications in redux, functional programming. Hence in order for a function to be classified as pure, it must fulfill certain rules:
1) Given the same arguments, it returns the same result: let us illustrate this
const sum = (a, b) => a + b
sum(1, 3) // 4
sum(1, 3) // 4
sum(4, 2) // 6
sum(4, 2) // 6
looking at sum
produces the same result, when we pass the same argument to it, sum
satisfies the first rule of pure function.
2) Pure function does not produce side effects:- sides effects could be mutating external state, logging(e.g console.log), API request, I/O operations.
const sumWithLog = (a, b) => {
console.log(a, b)
return a+b
}
const arr = [2, 3, 4];
const addToArr = (arr, a) => {
arr.push(a);
return arr;
}
console.log(addToArr(arr, 5)) // [2, 3, 4, 5];
console.log(addToArr(arr, 5)) // [2, 3, 4, 5, 5];
Looking at sumWithLog
it technically violates the second rule by definition despite it obeys the first rule, while addToArr
is not also a pure function because it's mutating external state. let's make impure addToArr
to become pure by adjusting the code,
const arr = [2, 3, 4];
const addToArr = (arr, a) => {
const arrCopy = [...arr] // line 1
arrCopy.push(a);
return arrCopy;
}
console.log(arr) // [2, 3, 4]
console.log(addToArr(arr, 5)) // [2, 3, 4, 5];
console.log(addToArr(arr, 5)) // [2, 3, 4, 5]
console.log(arr) // [2, 3, 4]
Looking at the code snippet above, addToArr
obeys our first rule and second rule, our result is now predictable. At line 1 we make a copy of arr
by spreading arr
elements which gave us the capability to not mutate the original arr
. it's also important to note that spread operator does shallow copying of values.
3) Pure functions depends only on the arguments passed to them. What this actually means is a pure function is self-contained, it does not depend on values outside its scope to determine its final value. let's illustrate this:
const b = 6
const sum1 = (a) => (a + b) // line 1
const sum2 = (a, b = 1) => (a + b) line // 2
sum1
is considered an impure function because it takes value outside its scope to determine the final result which could be changed. The value of b
in sum1 could easily be changed by external code which makes the function to unpredictable. sum2
corrects the defect in sum1
.
Hence for a function to consider a pure function, the three rules above must all be satisfied, violation of any automatically makes the function impure. Finally, pure functions make our code more predictable and testable.