“Pure function” is one of those terms that might be intimidating at first, but the concept is actually quite simple. In this email, I’ll quickly define what pure functions are and why they’re good.

What is a Pure Function?

To be considered pure, a function must meet two criteria: it must have (1) identical output for identical inputs and (2) no side effects.

Let’s dig deeper into each of these criteria.

Identical output for identical inputs

Let’s first consider a function that doesn’t have identical output for identical inputs: the greet function below:

let greeting = "Hello" ; function greet ( name ) { return greeting + " " + name ; } console . log ( greet ( "Paul" )); // Hello Paul

We see here that the output of the greet function can change even if the input name to that function stays the same (all we have to do is change the greeting variable value).

How can we make the output identical for identical inputs? It’s as easy as just ensuring the greeting is also an argument for the function:

function greet ( greeting , name ) { return greeting + " " + name ; } console . log ( greet ( "Howdy" , "Paul" )); // Howdy Paul

Now, there’s nothing we can do to make the greet function return a different result unless we change one of its input arguments.

No side effects

A side effect is when the function changes something outside the scope of the function. Again, let’s take a look at an example of a function that violates the “no side effect” rule:

const user = { username : "bob1234" ; } let isValid = false ; function validate ( user ) { if ( user . username . length > 4 ) { isValid = true ; } }

We can see that we’re clearly changing the isValid variable, which is outside the scope of the validate function. (Note: if your function doesn’t return anything, that’s a strong indication that it might have side effects!).

So, how do we remove this side effect? Rather than changing an external isValid variable, we can return whether or not the user is valid from the function itself:

const user = { username : "bob1234" ; } function validate ( user ) { return user . username . length > 4 ; } const isValid = validate ( user );

And there you have it–our validate function no longer changes anything outside its scope.

Why are Pure Functions Good?

You probably have some intuition that these pure function concepts are good, especially if you’ve experienced first-hand why they’re bad. The more you can encapsulate logic, the easier it is to test, and the easier it is to change without worry about what that change will affect.

Let’s consider testing our greet function. In its initial form, we could make the following assertion:

describe ( "greet" , function () { it ( "shows a greeting" , function () { expect ( greet ( "Jane" )). toEqual ( "Hello Jane" ); }) })

However, if we change our greeting variable to be “Hi”, our test fails! This shouldn’t happen; out function is working correctly but it’s failing because it depends on a variable outside its scope. When we make the greeting an argument to the greet function, this problem goes away as all the information we need to use the function must be supplied as arguments within the test.

Now let’s consider our user validation example. How would we even go about testing it? It might be hard to assert a variable external to the function is changed. Furthermore, if we changed the default value of the isValid variable to true , the function fails. It definitely doesn’t feel right to have this external variable change!

Conclusion

I hope this email provided some insight into what pure functions are and why they’re useful!