DEV Community

Cover image for Functional JavaScript in 3 minutes: Is this function pure? Quick checklist ✅
Michał Brzoza
Michał Brzoza

Posted on • Originally published at codingoals.com

Functional JavaScript in 3 minutes: Is this function pure? Quick checklist ✅

Remember functions from math class?

function graph

For every x, there is a corresponding y.

FP paradigm is based on this idea of treating functions as mathematical functions.

Functions should:

  • consistently produces the same output for a given input
  • does not cause observable side effects.

Everything that is not a function is a procedure.

Programming is not math. But certainly, we can take advantage of math in programming. There are many benefits of doing so, such as:

  • Easier to reason about
  • Easier to test
  • Easier to debug

The whys are a separate discussion. But for now, let's focus on what the headline of this article promised - the pure functions.

Pure Function

Always has output, for every input.

Check out the code below: What if the input is null or undefined? Or it's anything other than 'sunny,' 'rainy', or 'snowy'?



function getWeatherIcon(weather) {
  if (weather === 'sunny') {
    return '🌞';
  } else if (weather === 'rainy') {
    return '🌧';
  } else if (weather === 'snowy') {
    return '';
  }
}
Enter fullscreen mode Exit fullscreen mode

Below, we added a fallback. Now, we have an output for every input.



function getWeatherIcon(weather) {
  if (weather === 'sunny') {
    return '🌞';
  } else if (weather === 'rainy') {
    return '🌧';
  } else if (weather === 'snowy') {
    return '';
  } else {
    return '🤷'; // fallback 
  }
}
Enter fullscreen mode Exit fullscreen mode

What about narrowing the range with TypeScript?

function getWeatherIcon(weather: 'sunny' | 'rainy' | 'snowy') {
  if (weather === 'sunny') {
    return '🌞';
  } else if (weather === 'rainy') {
    return '🌧';
  } else if (weather === 'snowy') {
    return '';
  }
}
Enter fullscreen mode Exit fullscreen mode

This function is pure - you are not allowed to pass anything other than 'sunny', 'rainy' or 'snowy' ✅

Of course, technically speaking, TypeScript is just a linter, and we are all going to hell for doing FP in JS, but this solution is good enough. This is a contract for developers, making it clear what the function can accept and what it returns.

The output is always the same for the same input.

Pure function - same input, same output:



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

Impure function - same input, different output:



function getRandomEmojii(emojis) { 
  return emojis[Math.floor(Math.random() * emojis.length)];
}

getRandomEmojii(['🌞', '🌧', '']);

Enter fullscreen mode Exit fullscreen mode

No side effects

What is a side effect? Any change in the system that is observable to the outside world, such as:

  • Modifying shared state (such as global variables, variables in parent functions scope)


let post = {
  title: 'Pure functions are awesome!',
  comments: [],
};

function addComment() {
  post.comments.push('Great post!');
}

addComment(post);
Enter fullscreen mode Exit fullscreen mode

addComment(post) is impure call - it modifies the outside world - global variable post



function addComment(post, comment) {
  post.comments.push(comment);
}

addComment(post);
Enter fullscreen mode Exit fullscreen mode

Still impure - comments.push() modifies the input object

What can we do about it?



function addComment({post, comment}: CommentDTO) {
  return {
    ...post,
    comments: [...post.comments, comment],
  } 
}
Enter fullscreen mode Exit fullscreen mode

Now, we are not modifying the input, we create a new copy of an object with the new copy of comments array. ✅

  • Making a network request:


function createComment({title, body, author}: Comment) {
  return someAjax('https://api/comments', {
    method: 'POST',
    body: JSON.stringify({title, body, author})
  });
}

Enter fullscreen mode Exit fullscreen mode

As side effects are not avoidable in real-world applications, the way we often deal with that is by isolating pure and impure parts of the code.
For instance, instead of calling someAjax directly, we could return a function that will eventually make a network request:



function createComment({title, body, author}: Comment) {
  return () => someAjax('https://api/comments', {
    method: 'POST',
    body: JSON.stringify({title, body, author})
  });
}

const createCommentRequest = createComment({
  title: '...', body: '...', author: '...'
});
// no side effects yet

Enter fullscreen mode Exit fullscreen mode

By doing so, we can handle all side effects in one place, in a controlled way, and keep the rest of the code pure. ✅

Those were some examples of side effects, but there are many more. The point is, pure function should not alter, rely on, or interfere with anything outside of itself, in a way that is observable to the outside.

What it should do is reliably produce the same output for the same input, every time.

TLDR, is this function pure?

  • Always has an output for every input. ✅
  • The output is always the same for the same input. ✅
  • Does not alter/rely on the outside world ✅

Top comments (0)