Improving our code with pure functions

JavaScript

Our code can get quite complicated. The events handlers are triggered, the HTTP calls are made. We make a lot of stuff happen that we don’t know the outcome of. The more dependencies, the more difficult it is to predict how the function might behave. In this article, we strive to make our code more testable and readable using the concept of pure functions.

The idea of pure functions comes from the functional programming paradigm. It gets more and more popular, and JavaScript is a fitting language for using the above style.

No side effects!

One of the main issues that we can experience with unpure functions are so-called side-effects. It is any influence on the outside from within a function.

The above function is far from pure. We can’t determine its outcome at all, because it relies on the outside world: the API. It is an example of a side effect. We can’t really write any unit tests for it in a straightforward way. Making real HTTP requests in our unit tests is not something we should do.

We can’t also give up on making an HTTP call. We can, on the other hand, delegate part of the logic to an additional function.

There is still one wrong with our   function. Can you spot it?

It mutates the arguments! We should never do that when aiming to write pure functions.

Looks better, doesn’t it? We create a brand new array and return it. There is one issue though. With   we create a shallow clone of the users.

With the code above, we modify objects that are both in the original and in the cloned array.

By writing code as above, we break the rule of not muting the arguments the function receives. To solve such problems, we can create a deep clone.

If you want to know more, check out Cloning objects in JavaScript. Looking under the hood of reference and primitive types

With the above approach, we don’t modify the users from the function arguments. We can also create a new instance of a user when adding the posts array.

What approach do you think is better in this situation?

When looking for potential impure functions in your codebase, it might be a good idea to look at functions that don’t return anything. They might have some side effects!

No external dependencies!

When aiming to write pure functions, you need to keep an important rule in mind: output depends only on the input. Our pure functions shouldn’t access any variables outside, such as global variables.

The function above is not pure anymore because we depend on outside factors. We should provide all the dependencies in the arguments.

When refactoring your code, a function that does not have any arguments might be a good candidate for an impure function.

No indeterministic output!

When a function is deterministic, it always returns the same output for a given input. An excellent example of an indeterministic function is the one that uses random numbers. While sometimes it is necessary, it would be tough to test such function.

Let’s take a look at this very simple example above. Unfortunately, it might be quite difficult to write some tests for it, because it is not deterministic by design. What we could do is to break it down into two separate functions.

Now, one of the above functions is deterministic: the   always returns the same output for a given input.

Now we can test a part of our logic easily, even though it is indeterministic as a whole.

Summary

In this article, we’ve gone through the concept of pure functions. We’ve learned that such a function should not have any external dependencies. Also, it shouldn’t perform any side effects. On top of that, it should be deterministic. We’ve also acquited the knowledge on how to fix the above issues. Thanks to that, our functions are now more testable and we can depend on them more.

avatar
  Subscribe  
Notify of