With this article, we will explore what pure/impure functions are and their differences in JavaScript. Let's dive in..
What is a function?
In the ocean of JavaScript, functions are an essential building block for creating complex and interactive apps.
One of the most common uses of functions is mapping, where they take input values and produce corresponding output values. This allows for the transformation and manipulation of data, which is critical in creating dynamic user experiences.
What is a pure function?
A pure function is a function where the return value is only determined by its arguments without any side effects. Let's say, if you call a function with the same arguments n
number of times and n
number of places in the application then it will always return the same value. Pure functions do not modify any external state, such as variables or objects outside the function. They only use the input arguments to perform calculations and return the result.
To show you what I mean..
Below function takes two numbers, a
and b
, as input arguments and returns their sum. This function is a pure function because it always produces the same output for the same input and does not have any side effects.
function add(a, b) {
return a + b;
}
What is an impure function?
An impure function is a type of function, given the same input, may produce different output at different times. This is because impure functions can rely on external factors such as changes in global variables or other functions, that affect their behavior and output.
Below function modifies the external variable counter by increasing its value by one each time it is called. This means that it has side effects and is not a pure function.
let counter = 0;
function increment() {
counter++;
return counter;
}
Why are pure functions useful?
Pure functions have several advantages over impure functions:
1) Predictability
Because pure functions always produce the same output for a given input, they are predictable and easy to reason about. Its easier to test and debug and reduces the likelihood of unexpected behavior in your code.
2) Reusability
Pure functions are modular and self contained, meaning that they can be reused in different parts of your codebase without affecting other parts. This can save time and reduce the amount of code you need to write.
3) Parallelization
Because pure functions do not modify external state, they can be executed in parallel without worrying about race conditions or other synchronization issues. This can lead to faster and more efficient code.
How to create pure functions in JavaScript?
1) Use only input arguments
Pure functions should use only their input arguments to perform calculations and return the result. They should not modify any external state or rely on external variables.
2) Avoid side effects
Pure functions should not have any side effects, such as modifying external variables or objects. They should only return a value based on the input arguments.
3) Avoid global state
Pure functions should avoid using global state, such as global variables or objects. This can make your code less predictable and harder to debug.
4) Return a value
Pure functions should always return a value based on the input arguments. They should not rely on external variables or objects to produce their output.
Pure vs Impure Functions
Inbuilt pure functions in JavaScript:
Math.abs(): Returns the absolute value of a number.
Math.ceil(): Returns the smallest integer greater than or equal to a given number.
Math.floor(): Returns the largest integer less than or equal to a given number.
Math.max(): Returns the maximum value from a set of numbers.
Math.min(): Returns the minimum value from a set of numbers.
Math.round(): Returns the nearest integer to a given number.
Math.sqrt(): Returns the square root of a given number.
parseInt(): Converts a string to an integer.
parseFloat(): Converts a string to a floating point number.
JSON.parse(): Converts a JSON string to a JavaScript object.
Array.prototype.concat(): Returns a new array that contains the elements of the original array plus any additional elements that were passed in as arguments.
Array.prototype.slice(): Returns a new array that contains a portion of the original array, specified by a start and end index.
Array.prototype.map(): Returns a new array that is the result of calling a provided function on each element of the original array.
Array.prototype.filter(): Returns a new array that contains only the elements of the original array that satisfy a provided testing function.
Array.prototype.reduce(): Returns a single value that is the result of applying a provided function to each element of the array.
String.prototype.toUpperCase(): Returns a new string that contains the original string in all uppercase letters.
String.prototype.toLowerCase(): Returns a new string that contains the original string in all lowercase letters.
Inbuilt impure functions in JavaScript:
Math.random(): Returns a random number between 0 and 1. This function relies on external state which is the current state of the random number generator and its output changes every time it is called.
Date.now(): Returns the current timestamp which is the number of milliseconds that have elapsed since January 1, 1970
. This function relies on external state which is the current time and its output changes every time it is called.
console.log(): Writes a message to the console. It does not return a value but it has a side effect of logging information to the console.
setTimeout(): Executes a function after a specified delay which is specified in milliseconds. This function has a side effect of scheduling a function to be executed in the future.
setInterval(): Executes a function at a specified interval which is specified in milliseconds. This function has a side effect of scheduling a function to be executed repeatedly at a fixed interval.
document.write(): Writes HTML content to the document. It does not return a value but it has a side effect of modifying the document.
Math.floor(Math.random() * (max - min + 1) + min): Returns a random integer between a given range. This function relies on external state which is the current state of the random number generator and its output changes every time it is called.
Math.pow(): Returns the result of raising a number to a given power. While this function is mathematically pure, it may result in floating point rounding errors that can make its output impure.
โค Motivation:
๐Support
Please consider following and supporting us by subscribing to our channel. Your support is greatly appreciated and will help us continue creating content for you to enjoy. Thank you in advance for your support!
Top comments (49)
Interestingly, due to the way JS works, it is possible that this function:
could cause side effects, and even return a different value each time it's called - even with the same inputs. It all depends upon what
a
andb
are. So, in a VERY strict sense it can be argued that it isn't pure. I'll explain further if you don't know what I'm getting at.Different values each time:
Side effects:
Well on the first example, the inputs are not the same in both executions, because
performance.now()
returns a value, hence you are callingadd(1, 22278);
the first time andadd(1, 49557);
the second one. By that meansadd
is still a pure function in the first example.I get your point on the other one and there are tones of nuances into this topic, in this specific use-case TS helps a lot:
So you end up with fine tuned control on how the given function is used. Of course you can still do dynamic stuff and trick everything in runtime but still you need to explicitly put effort into breaking it most of the time (specially if the types are well defined everywhere).
Best regards
Not at all, the inputs are identical each time. The second of which is an object, with an unchanged method. Nothing about the input to the
add
function has changed on the second call. JS itself will call thevalueOf
function during coercion - after it has been passed in.We both know each other from the community and I'm pretty sure you understand that abstracting an impure function such
performance.now()
doesn't avoid getting different values each time you call it (to a precision of 5 milliseconds in this case). I don't have time right now to discuss with troll statements to stress it further (even though you know I like it a lot)The usage of
valueOf
in the example is pretty neat, though ๐You've misunderstood the code. I was just pointing out that the inputs to
add
on both calls are the same. I'm actually curious if it would be possible to write a 'true' pure function in JS that would take into account all the coercion quirks ๐คYes, I understood that.
What I'm trying to point is that you did the weirdest function composition (as reference to the readers; an approach where the result of one function is passed on to the next function, which is passed to another until the final function is executed for the final result) I've ever seen to twist the topic ๐ but it keeps being just that, and the
add
function keeps being pureThe "hack" if you will, of overriding the valueOf property referencing it to a function which happens to retrieve a different value each time... Was a pretty neat trick, I've to confess ๐๐ผ๐
I think you're confusing values and references. A pure function produces the same output if the input values don't change. In your example you dont change the input reference, but the reference can be a variable, which can hold different values. If you hard code the values into the pure function call, it will produce the same return value each time even in JavaScript.
The input values are not changing at all, but the values I use induce JS to call other functions (via it's coercion behaviour) - despite that not being entirely obvious. I'm pointing out that a seemingly pure function can (in JS) be made to behave in an impure manner.
Now lets try with some execution delay making performance.now() produce different values
As you can see, your example was entirely fabricated, which we have come to expect because you are a troll. Shit, I fell for it again...
Your example is entirely different - you are forcing the coercion to happen outside, resulting in something different to my example being passed in to the
add
function. The fact is I pass in identical values - a number and an object, and get different values out. I'm just pointing out some of the interesting behaviour of JS. Not trolling at all, educating.Also,
performance.now()
is entirely irrelevant. You could put anything here that returned a different value each time. This was just the first thing that popped into my head.Aww you messed up, you admitted in your last sentence that the value is different each time, thus the rules of pure functions are being respected. Different input, different output.
Didn't mess up - I said something that 'returned' a different value. What is being passed in is an identical object, with an unchanged method. The fact that JS calls that method after it is passed in doesn't change the fact that it was the same thing passed in.
Yes you tried to trick it by passing in an object with a valueOf method. Pretending the valueOf method doesnt mean anything, however it does mean something, it means the value of your variable changes, as you have already admitted. Therefor, different input value -> different output. I will admit that the moment the reassignment happens, is inside the purefunction, so its a clever trick, however, as was already demonstrated, if you force the reassignment outside the pure function, it becomes appearant that it is infact a variable with a different value each time.
If you force the coercion outside, then you are very definitely passing in a different value. My initial comment merely pointed out that if we take the definition of a pure function VERY strictly, then due to the way JavaScript works there are circumstances under which the function can be made to behave in an impure manner.
These aren't 'tricks', they're part of the way JS works and are valuable to understand ๐
Ok yes, I see the value of (pun intended) understanding how JS works. I still don't agree that it strictly violates the pure function rules though. Other languages have similar features, afaik pretty much every OOP language has a toString method that can be abused in the same way. The responsibility falls on the developer, when such methods are used to supply input values to a pure function imho.
I would say that quirks are not a desirable feature of a language. In fact, your example has highlighted a disadvantage of typeless languages.
Using TypeScript would have made your edge case, redundant.
Typed languages, even ones that are transpiled, allow every member of a team to understand precisely, how every data structure, should work. This makes it less likely that bugs will arise, in the code.
Possibly not desirable, but still a part of the language - not necessarily an advantage or a disadvantage. I also have long experience with typed languages (I've been programming for 39 years), and greatly enjoy the freedom that languages like JS give you. It's far easier to be creative, faster. When I first discovered them, it was like a breath of fresh air. Working with them requires a different mindset for sure, but I think it's worth it.
A valid point for typeless languages.. JS can be beneficial in certain situations and can do unexpected things due to some factors..
On otherside, TS don't completely stop the bugs. Let's say, With the strict typing, developers still can make mistakes in their code or have unexpected behaviors due to some factors..
I would like to add that the selection of language depends on the specific needs of a project. Both languages have their own strengths & weaknesses..
Thanks you for your feedback.
Thanks a ton to @jonrandy to come up with an interesting topic for this article..
Programming languages never do 'unexpected' things. They do precisely what they're told according to how they function. If anything seems 'unexpected'' it is mostly likely due to an error in your logic, or a gap in your knowledge of the language in question ๐
Yes of course, it's not a fault of programming language but developers. Due to misunderstanding of language. It can happen with many languages not just JS or TS..
It is unfortunate to hear that you are having a bad experience. There are many things that can be done to help improve the situation. First, make sure you have found the right resources to assist you.
Hello @jonrandy
Your statement is wrong in my opinion..
The use of
performance.now()
inb
is a special case that can cause the function to produce different results for the same inputs but this is not somethin that would typically be encountered in most real world use cases..Below is a Pure Function in JavaScript, designed to take the argument in number format only. If you pass
5
,5
it will return10
. Very simple example of pure function..when you pass the object/function as an argument which is completely wrong usage of
add
function. It's not designed to handle the object typed values. At the end,add
function is still a pure function..In most cases the inputs to a pure function will be primitives or immutable data types and the function will operate solely on those inputs without any side effects.. when we talk about the pure functions we assum that the inputs are of a reasonable type and that the function does not have any side effects beyond its return value.. the edge cases like the ones you have presented, they do not fundamentally change the idea of pure functions..
My comment was in no way designed to criticise your post, or to say that you were wrong. I just wanted to highlight that if we take the definition of a 'pure' in a very strict sense - then it is possible to say that (in JS at least) a lot of functions that look pure could actually be considered impure due to the language's nature.
These quirks of JS are not 'poison', or things to be avoided - they are an integral part of how the language works. Learning about them will bring you a deeper understanding and will benefit you when trying to work out the seemingly 'odd' behaviour that JavaScript sometimes exhibits. They can also be put to good use, and achieve things that you wouldn't even think were possible.
Over time there does seem to be a trend towards preaching 'readable' code at the expense of neglecting language features deemed 'difficult' or 'esoteric'. This is worrying as it can only ever lead to a diminishing knowledge of the languages, and decreasingly skilled programmers. Learn all you can about a language, embrace its quirks and inner workings, use them to your advantage - you'll be a much better developer for it. Also, remember that 'readability' is subjective.
@jonrandy
I respect you and your comment/feedback. I said your statement
const b = { valueOf: () => performance.now() }
was wrong in my opinion, not you or your concern.Your concern was correct but If you pass wrong values to a function, it may cause unexpected things.. A pure function can't handle every cases. There are so many ways to tweak the behavior of a pure function.
Thank you for bringing this scenario up..
I have fixed that pure function using
typeof
validation in JS only:Is above function a pure or an impure function? Your thoughts??
You may have designed it to take 'number' values - but with JavaScript's in-built coercion behaviours, it is perfectly possible to create other types of object that have a numeric value (can be co-erced to a number). There are completely valid use cases for this, and your function will still work perfectly fine with objects such as these.
I gave an extreme (non-practical) example to illustrate a point, but is not wrong to use functions such as
add
above with different types of inputs that actually have numeric 'value' via coercion. This is a key feature of JS.Yes correct.. Our function should be capable to handle such expected cases to bypass the coercion behaviours..
Maybe, but bypassing instead of embracing the coercion behaviours will reduce the utility of your function and make it unnecessarily complex.
Ofcourse it will become more complex..
Will it make sense for pure functions, if I write it down as below:
1) With function usage explanation in JavaScript
2) With function usage explanation + typeof validation in JavaScript
3) With function usage explanation in TypeScript
Thank you, that's insane, I hate and love JavaScript
@jonrandy
just like how a human body works smoothly when given healthy foods. On otherside, pure functions work as intended when given appropriate inputs and operated on without any side effects. But but but, just as giving a human unhealthy or poisoned foods can lead to negative consequences. Same way, providing impure inputs or using impure functions can lead to unexpected results. That;s why, it is important to use pure functions with appropriate inputs to ensure that the program operates smoothly as intended..
valuable article
Thanks for your feedback bro
This is a great walkthrough of pure functions.
This might not apply to JS but in some languages, e.g. C#, it's possible to change the next line that gets run in the debugger. Pure functions are great because of their idempotence (i.e. the same inputs give the same outputs, as mentioned in the article), meaning that they can be run over and over if necessary during the debugging process to understand what's going on.
By they way, how did you create the cool animated graphics?
Thank you my friend for bringing up an interesting point of pure functions with debugging.. BTW I use canva.com to create animated graphics..
Interseting article
Thank you bro
One more benefit of pure functions - they can be memoized, b/c of their nature to produce the same result for the same arguments
Make sense. Thank you for sharing your thoughts
ะกongratulations ๐ฅณ! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up ๐
Thanks a ton man
Interesting, thx!
Thank you boss