DEV Community

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

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

Code of Relevancy on March 28, 2023

With this article, we will explore what pure/impure functions are and their differences in JavaScript. Let's dive in.. What is a funct...
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