What is Pure Function in JS?
A pure function is one that does not have side effects, meaning they do not change any external state or modify external variables. They always return the same output for the same input, resulting in determinism. Pure functions are important in functional programming because they enhance code predictability, testability, and maintainability.
Some key characteristics of pure functions include:
No side effects: Pure functions do not change any external state and do not modify external variables. As a result, these functions can be easily reproduced, tested, and maintained.
Deterministic: Pure functions always provide the same output for the same input. This makes it easy to understand their behavior and reason about them.
security: Pure functions work as a means to increase code safety. This reduces the risk of crashes or bugs in the system. It also makes code testing easier.
Here are some examples of pure functions in JavaScript:
- Addition function:
function add(a, b) { return a + b; }
This function takes two arguments and returns their sum. It is a pure function because it doesn’t change any external state and always provides the same result for the same input.
- Array concatenation function:
function concatenateArrays(arr1, arr2) { return arr1.concat(arr2); }
This function takes two arrays and returns a new array that contains the elements of both arrays. It is a pure function because it doesn’t change any external state and always provides the same result for the same input.
- Date formatting function:
function formatDate(date) { const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); return `${year}-${month}-${day}`; }
This function takes a date object and returns a string in the “YYYY-MM-DD”
format. It is a pure function because it doesn’t change any external state and always provides the same result for the same input.
- String capitalization function:
function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); }
This function takes a string and returns a new string with the first character capitalized. It is a pure function because it doesn’t change any external state and always provides the same result for the same input.
Pure Functions and Side Effects with Examples:
As a software programmer/developer, you write source code to produce an output based on the inputs. Usually, you write functions
to perform the tasks based on inputs and produce an output.
We need to make sure these functions are:
Predictable: It produces a predictable output for the same inputs.
Readable: Anyone reading the
function
as a standalone unit can understand its purpose completely.Reusable: Can reuse the
function
at multiple places of the source code without altering its and the caller’s behavior.Testable: We can test it as an independent unit.
A Pure Function has all the above characteristics. It is a function
that produces the same output for the same input. It means it returns the same result when you pass the same arguments
. A pure function shouldn’t have any side effects to change the expected output.
The function sayGreeting()
below is a pure function. Can you please guess why?
function sayGreeting(name) { return `Hello ${name}`;
}
It is a pure function because you always get a Hello name
as output for the name
pass as an input. Now, let us see the same function with a bit of change.
let greeting = "Hello";
function sayGreeting(name) { return `${greeting} ${name}`;
}
Is it a pure function? Well, No. The function’s output now depends on an outer state called greeting
. What if someone changes the value of the greeting
variable to Hola? It will change the output of the sayGreeting()
function even when you pass the same input.
// When greeting is "Hello"
sayGreeting('Alex'); // Returns, "Hello Alex" // When greeting is "Hola"
sayGreeting('Alex'); // Returns, "Hola Alex"
So, here we have seen the side-effect of depending on an outer state value that may change without the function being aware of it.
A few more classic cases of the side effects are:
Mutating(changing) the input itself.
Querying/Updating DOM
Logging(even in the console)
Making an XHR/fetch call.
Any operation that is not directly related to the final output of the function is called a Side Effect
. Now let us see an impure
a function where we mutate the input and do something that we are not supposed to in a pure function
.
function findUser(users, item) { const reversedUsers = users.reverse(); const found = reversedUsers.find((user) => { return user === item; }); document.getElementById('user-found').innerText = found;
}
The above function takes two arguments
, a collection of users(an array) and an item to find in the array. It finds the item from the end of the array by reversing it. Once the item is found in the array, it set that value as a text to an HTML
element using DOM methods
.
Here we are breaking two essential principles of the pure function
.
So, what kind of problem we can anticipate? Let’s see. A caller will invoke the findUser()
function in the following way:
let users = ['Tapas', 'Alex', 'John', 'Maria'];
findUser(users, 'Maria');
At this stage, the caller may not know that the function is making a DOM operation unless the caller reads the findUser()
function code. So, readability is compromised. The function’s output is performing an operation that is not related to the final output.
Also, we have mutated the input array. Ideally, we should have cloned the input and then mutated
(reversed) the copy for the find operation. Let us now make it a pure function.
function findUser(users, item) { // Create the clone of users array and then reverse const reversedUsers = [ ...users].reverse(); // Find the element in the cloned array const found = reversedUsers.find((user) => { return user === item; }); // Return the found element return found;
} let users = ['Alex', 'John', 'Maria'];
let found = findUser(users, 'Maria');
Now the findUser()
the function is a pure function. We have removed the side effects of mutating the input, and it returns the intended output. Hence the function is readable, testable as a unit, reusable, and predictable.
Conclusion
Embracing functional programming concepts like a pure function, and reducing side effects will make your code better to manage and maintain. It means lesser bugs, quick identification of issues, isolating problems, increased reusability, and testability. Generally, pure functions avoid working with mutable data and ensure the same output for the same input. They are ideal for writing predictable and maintainable code.