Understanding pure and impure functions is a simple transition into cleaner, more role-based and testable code. In this post we’ll explore pure and impure functions by looking at a simple Body Mass Index (BMI) calculator that estimates your “healthy weight” by some simple input factors of height and weight. BMI isn’t considered the most reliable tool for checking your weight, but that’s not the point of this article ;)
Table of contents
Note: if you’re not familiar with
cmunits, use something like
cmto try it out.
Before we begin, let’s clarify what “impure” and “pure” functions really mean in programming terms.
A pure function is much easier to comprehend, especially as our codebase may scale, as well as role-based functions that do one job and do it well. Pure functions don’t modify external variables/state/data outside of the scope, and returns the same output given the same input. Therefore it is deemed “pure”.
Let’s refactor our BMI calculator that I’ve created in a fully impure fashion, into multiple functions that make use of pure functions.
HTML and submit event
Here’s the markup I’ve created to use for capturing the user’s input data:
And as a base, we’ll attach an event listener as a base and
preventDefault on the
The live output (which doesn’t work yet) here:
We’ll cut out the IIFE and event handler fluff for now and focus on the
That’s all our function contains, and once you enter your height/weight it’ll update the DOM with those results. Now, this is what I would personally consider a bit of a mess, and extremely difficult to debug and understand the role of the function. Let’s explain what’s happening here with some code comments:
Upon first look, this is absolutely fine in terms of the fact that “it works”. However if we began to scale this, we would end up with a monstrosity codebase with a bible of procedural code that is very easily broken.
We can do better, but here’s the live demo for this implementation:
Before we can start using pure functions, we need to decide what functions will be pure. In the above and 100% impure implementation we did way too many things in a single function:
- Read values from the DOM
- Parsed values to numbers
- Calculated the BMI from the parsed values
- Conditionally checked the BMI result and assigned the correct message to an undefined variable
- Wrote values to the DOM
To “go pure”, we’ll implement functions that handle these actions:
- Parse values to numbers and calculate the BMI
- Return us the correct message for binding to the DOM
Let’s start with the input value parsing and calculating the BMI, specifically addressing this section of code:
This deals with
parseInt() and the formula to calculate the BMI. This is not very flexible and likely very error prone when at some point in an application we’d come to refactoring or adding more features.
To refactor, we’re only going obtain each input’s value property alone, and delegate those into a
getBMI function would be 100% pure in the fact that it accepts arguments and returns a new piece of data based on those arguments. Given the same input, you’ll get the same output.
Here’s how I’d implement the
This function takes the
height as arguments, converts them to Numbers through
parseInt and then performs the calculation for the BMI. Whether we pass a String or Number as each argument, we can safety check and
parseInt regardless here.
Onto the next function. Instead of
else if logic to assign the
healthMessage, we’ll create the expected result to look like this:
Again, this is much easier to reason with. The implementation of
getHealthMessage would look like this:
Putting everything together, we have this:
You can see how much clearer this becomes. It also means we can test the
getHealthMessage functions on their own, without any external variables being needed. This means our “impure”
onSubmit function becomes much clearer and easier to extend, refactor without breaking any isolated pieces of logic that may have before relied on variables in the lexical scope(s).
The final output with a mix of impure and pure functions: