DEV Community

Cover image for You Need to Know About Pure Functions & Impure Functions in JavaScript
Code of Relevancy
Code of Relevancy

Posted on

You Need to Know About Pure Functions & Impure Functions in JavaScript

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.

What is a pure function

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;
}
Enter fullscreen mode Exit fullscreen mode

Pure Functions


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.

What is an impure function

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;
}
Enter fullscreen mode Exit fullscreen mode

Impure Functions


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

Pure vs Impure Functions in JavaScript


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:

Do more


๐Ÿ€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!

YouTube
Discord
GitHub

Dour Darcel #8740

Top comments (49)

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

Interestingly, due to the way JS works, it is possible that this function:

function add(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

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 and b 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.

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

Different values each time:

const a = 1 // a number
const b = { valueOf: () => performance.now() } // an object with a method

// Pure function?
function add(a, b) {
  return a + b
}

console.log(add(a, b))  // 22279
console.log(add(a, b))  // 49558 - same inputs, different result
Enter fullscreen mode Exit fullscreen mode

Side effects:

const a = 1  // a number
const b = { valueOf () { console.log('Hello world'); return 2; } }  // Object with method

// Pure function?
function add(a, b) {
  return a + b
}

console.log(add(a, b))  // 'Hello world' (side effect) then 3
Enter fullscreen mode Exit fullscreen mode
Collapse
 
joelbonetr profile image
JoelBonetR ๐Ÿฅ‡ • Edited

Well on the first example, the inputs are not the same in both executions, because performance.now() returns a value, hence you are calling add(1, 22278); the first time and add(1, 49557); the second one. By that means add 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:

const a = 1  // a number
const b = { valueOf () { console.log('Hello world'); return(2); } }  // Object with method

// Pure function
function add(n1: number, n2: number) {
  return a + b
}

console.log(add(a, b)) // TypeError, Object !== number
Enter fullscreen mode Exit fullscreen mode

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

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

...hence you are calling add(1, 22278); the first time and add(1, 49557); the second one...

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 the valueOf function during coercion - after it has been passed in.

Thread Thread
 
joelbonetr profile image
JoelBonetR ๐Ÿฅ‡

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 ๐Ÿ˜‚

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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 ๐Ÿค”

Thread Thread
 
joelbonetr profile image
JoelBonetR ๐Ÿฅ‡ • Edited

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 pure

The "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 ๐Ÿ‘Œ๐Ÿผ๐Ÿ˜ƒ

Collapse
 
brense profile image
Rense Bakker

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.

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

Thread Thread
 
brense profile image
Rense Bakker
const a = 1
const b = { valueOf: () => performance.now() }

function add(a, b) {
  return a + b
}

const c = b + ''
const d = b + ''
// no change, due to no delay between execution of performance.now()

console.log(add(a, Number(c)), c) // 168.20000004768372, "167.20000004768372"
console.log(add(a, Number(d)), d) // 168.20000004768372, "167.20000004768372"
console.log(c === d) // true
Enter fullscreen mode Exit fullscreen mode

Now lets try with some execution delay making performance.now() produce different values

const a = 1
const b = { valueOf: () => performance.now() }

function add(a, b) {
  return a + b
}

const c = b + ''

console.log(add(a, Number(c)), c) // 148.80000019073486, "147.80000019073486"

const d = b + ''

console.log(add(a, Number(d)), d) // 148.90000009536743, "147.90000009536743"

console.log(c === d) // false

// different input values, different result
// pure function working exactly as expected
Enter fullscreen mode Exit fullscreen mode

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...

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

const a = 1 // a number
const b = { valueOf: () => window.a = ~~window.a + 1 } // an object with a method

// Pure function?
function add(a, b) {
  return a + b
}

console.log(add(a,b))  // 2
console.log(add(a,b))  // 3
console.log(add(a,b))  // 4
...
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
brense profile image
Rense Bakker

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.

You could put anything here that returned a different value each time.

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

Thread Thread
 
brense profile image
Rense Bakker

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.

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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 ๐Ÿ‘

Thread Thread
 
brense profile image
Rense Bakker

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.

Collapse
 
charlesr1971 profile image
Charles Robertson

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.

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy

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..

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ

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 ๐Ÿ‘

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy

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..

Collapse
 
corners2wall profile image
Corners 2 Wall

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.

Collapse
 
codeofrelevancy profile image
Code of Relevancy • Edited

Hello @jonrandy
Your statement is wrong in my opinion..

The use of performance.now() in b 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 return 10. Very simple example of pure function..

function add(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

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..

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy

@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:

/*
Returns the sum of two input numbers

@param {number} a - The first number to be added
@param {number} b - The second number to be added

@returns {string | number} - If both arguments are numbers, returns their sum as a number. If either or both arguments are not numbers, returns an error message as a string
*/
function add(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    return 'Both arguments must be numbers.';
  }
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Is above function a pure or an impure function? Your thoughts??

Collapse
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

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.

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy • Edited

Yes correct.. Our function should be capable to handle such expected cases to bypass the coercion behaviours..

Image description

Thread Thread
 
jonrandy profile image
Jon Randy ๐ŸŽ–๏ธ • Edited

Maybe, but bypassing instead of embracing the coercion behaviours will reduce the utility of your function and make it unnecessarily complex.

Thread Thread
 
codeofrelevancy profile image
Code of Relevancy

Ofcourse it will become more complex..

Collapse
 
codeofrelevancy profile image
Code of Relevancy • Edited

Will it make sense for pure functions, if I write it down as below:

1) With function usage explanation in JavaScript

/*
Returns the sum of two input numbers

@param {number} a - The first number to be added
@param {number} b - The second number to be added

@returns {number} - The sum of a and b
*/
function add(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

2) With function usage explanation + typeof validation in JavaScript

/*
Returns the sum of two input numbers

@param {number} a - The first number to be added
@param {number} b - The second number to be added

@returns {string | number} - If both arguments are numbers, returns their sum as a number. If either or both arguments are not numbers, returns an error message as a string
*/
function add(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    return 'Both arguments must be numbers.';
  }
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

3) With function usage explanation in TypeScript

/*
Returns the sum of two input numbers

@param {number} a - The first number to be added
@param {number} b - The second number to be added

@returns {number} - The sum of a and b
*/
function add(a: number, b: number): number {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jmfayard profile image
Jean-Michel ๐Ÿ•ต๐Ÿปโ€โ™‚๏ธ Fayard

Thank you, that's insane, I hate and love JavaScript

Collapse
 
codeofrelevancy profile image
Code of Relevancy

@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..

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
abhixsh profile image
Abishek Haththakage

valuable article

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thanks for your feedback bro

Collapse
 
Sloan, the sloth mascot
Comment deleted
 
Sloan, the sloth mascot
Comment deleted
 
Sloan, the sloth mascot
Comment deleted
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
ant_f_dev profile image
Anthony Fung

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?

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you my friend for bringing up an interesting point of pure functions with debugging.. BTW I use canva.com to create animated graphics..

Collapse
 
abhixsh profile image
Abishek Haththakage

Interseting article

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you bro

Collapse
 
phi1ipp profile image
Philipp Grigoryev

One more benefit of pure functions - they can be memoized, b/c of their nature to produce the same result for the same arguments

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Make sense. Thank you for sharing your thoughts

Collapse
 
fruntend profile image
fruntend

ะกongratulations ๐Ÿฅณ! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up ๐Ÿ‘

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thanks a ton man

Collapse
 
jermaynehudson34465 profile image
Jermayne Hudson

Interesting, thx!

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you boss