Today, I had a quirky function that needed writing. It involved far too many inputs and a lot more math (for a single function) than I would have liked. Even though the math itself is actually just basic trigonometry. After writing a few lines of code, it became quite obvious that this function would not be maintainable.
I was reminded of something I recently heard and fell in love with:
A Programming language is not for the computer, it is for people.
Another along the same lines:
If the computer doesn’t run it, it’s broken. If people can’t read it, it will be broken. Soon.
❤
Anyway, back to the function.
I had set my mind towards refactoring this thing. It was a typical refactor, break out some responsibilities, sprinkle in a little partial application, add some currying and a wrap it all up with function composition.
What I was left with was some very elegant and easily maintainable code. I reflected upon how rare it is for code to be written this way, which is why I am sharing today.
Disclaimer: A single article is nowhere near enough to cover all the functional programming techniques within. And we won’t be deep diving into any of them. My hope is that this high level overview will pique your curiosity with FP and interest you into starting your own journey.
So what am I doing anyway?
I am trying to calculate the probability of an event occurring. I know that as the velocity
of this event increases so does the probability
of the event occurring.
If the velocity
is below 1,000, then the probability
of occurrence is always 50%. If the velocity
is above 9,000, the probability
jumps to a maximum of 85%. It is easier to visualize with a chart, so here’s a chart.
And I want this all encapsulated into a single function.
This could be solved with a single line trigonometry, but it would look like a regex or machine language and today, we are writing code for people.
First I created a calibration object to transform the X
’s to the Y
’s.
const calibration = {x: { min: 1000, max: 9000 },y: { min: 50, max: 85 },}
Then I almost wrote this code to return the min Y
if the value is below the threshold and the max Y
if it is above.
But I really don’t like if
statements. If you find yourself curious as to why, then check out the article about it…
Rethinking JavaScript: The if statement_Thinking functionally has opened my mind about programming. It has given me a greater depth of understanding of code…_hackernoon.com
And there’s a better way of doing this using Math.min
and Math.max
. Since I need to use both I plan on just use Ramda’s clamp
method, which does exactly this.
Feature scaling, also known as normalizing, is a machine learning technique that I think will apply well here.
The gist of feature scaling is to scale the input (the X
) to a value between [0, 1]
. Then I will do the reverse and de-scale the number into the Y
scale.
You should ignore the majority of the code below, just take note of normalize
, specifically how it calls two functions, scale
and R.clamp
. denormalize
does the same, just in reverse.
Function composition is one of my favorite FP tools. It’s a technique to create new functions using existing functions.
normalize
and denormalize
are great candidates for function composition and we can use Ramda’s pipe
method to transform them into this:
Not vastly different, but we’re only half way there. We’ll reduce this more in the Currying & Partial Application section.
To continue learning more about Function Composition, check out my previous article on Function Composition.
Functional JavaScript: Function Composition For Every Day Use._Function composition has got to be my favorite part of functional programming. I hope to provide you with a good real…_hackernoon.com
Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. — WikiPedia
Currying allows us to easily partially apply arguments to scale
and R.clamp
.
Note: While I didn’t follow the 1 argument rule here, min and max should have also been broken out and separated. Though, it didn’t add any utility since both min and max will be supplied at the same time and it would have complicated the examples. Sometimes it’s better to go with the spirit of the law and not the letter of the law :)
Building the function is now super easy. This is because we have written all of the logic already. All that is left is to compose normalize
and denormalize
like this:
That’s it! Super simple.
All together it will look like this:
Observations:
Every function is an expression and not a block. The functions immediately return their value.
Every function is idempotent, meaning given the same inputs it will always produce the same output.
Every function is a pure function. The output it calculated from only the inputs.
The majority our functions do not contain any new code, they are simply composed of existing functions.
The simplicity is more than just cosmetic. The transformation I am working with is linear, but actually it’s probably closer to easeOutSine or easeOutCubic pictured below.
Because we have broken the code out into small pieces, it would be unbelievably simple to enhance this function. We simply have to add our transition into the R.pipe
pipeline. Here’s a few ways this could be done:
I do understand that “easier” is subjective and a lot of people who are unfamiliar with a functional programming style are probably having their minds melted right now.
You do have to know how to program functionally before you will also feel this way. Though once you do, this kind of style becomes the obvious and correct choice. The key here being once you know.
This is also not meant to be a tutorial. It is just a small lens into the world of functional javascript.
Hey, you made it all the way to the end! Thanks for listening to my rantings. I truly mean that.
Today I have demonstrated how to take functions that have been broken down into their single responsibilities can be easily composed into new functions.
I know I didn’t cover nowhere near enough, but I do have a lot of other functional programming articles that go into more details.
Latest stories written by Joel Thoms - Medium_Read the latest stories written by Joel Thoms on Medium. Computer Scientist and Technology Evangelist with 21 years of…_medium.com
I know it’s a small thing, but it makes my day when I get those follow notifications on Medium and Twitter (@joelnet). Or if you think I’m full of shit, tell me in the comments below.
Cheers!