FP in vanilla JavaScript: a rookie’s intro
If I were asked to perform a slam dunk I’d have to go through months if not years of trainings, blood and tears. Or I could just go to the Moon and easily do it right away since I’d weight six times less there.
The idea is that I can greatly simplify my work by moving to a space where the rules are more favourable for the problem at hand.
It just so happens that writing code is like building universes and going to the Moon is a viable thing to do.
The procedural universe
Let’s take a look at some code
The idea is that there’s a string containing an array of strings; procedural
parses it as JSON, then takes the first element of the array and formats it.
Problem is that in this universe we have to deal explicitly with all sorts of possible failures. In fact:
JSON.parse(string)
could fail in casestring
contained invalid JSON;array[0]
would returnundefined
in case the array was of length 0.
I already hear some haters back there saying I’m using a straw man by showing badly factored procedural code. So let’s refactor a bit
Or
Problem is that we are obliged to deal with try
s and if
s and cannot really hide them easily anywhere.
Well, let’s move to another universe and see what happens there.
The functional universe
Waaaaaaaaat the devil is going on there?
Let’s forget about parse
, first
, upper
andshout
for now. We are going to take care of lower level details later on.
The first thing to notice is that I don’t really know how to write functional code, so you got the rookie’s version of it. At the same time, I wanted to limit the amount of new concepts needed.
What the hell are map
and chain
doing? And what about Right
and Left
?
Let me tell you a secret: it’s not important to understand them. We just need to understand the intuition behind.
Dipping the toes in FP
First of all, let’s just forget what map
means
Ok, now we can talk.
Imagine for a moment that you stopped using primitives. Instead, you put all of your primitives inside of a box.
Now, map
is a function that allows you to interact with the boxed values. In other words, it allows running functions against values contained in a box and then return the boxed value.
On the other hand,chain
is a function that allows running functions against values contained in a box and return them unboxed.
Box
alone is as useful as a waterproof towel. But it enables us to understand other types of boxes like Right
and Left
.
They are both subtypes of Either
and are used whenever an operation could succeed and return something (e.g. Right('success')
) or fail (e.g. Left('some error')
).
Being subtypes of Either
enables polymorphism because they both implement map
and chain
. In fact, with a Right
, map
and chain
work as described above (i.e. Right
behaves like a Box
). With Left
, map
and chain
just pass the box forward and perform no work.
As I said, it’s not important to understand how they work internally. The most important part is the intuition: whenever an operation succeeds it returns a Right
, therefore subsequent map
s and chain
s will go on working with the contained value. Otherwise, a Left
would be returned and all subsequent operations wouldn’t be run.
This is extremely powerful because as long as a failure (e.g. null
, error
) is contained in a Left
the caller can be totally ignorant about it and the values contained won’t ever be used and create problems.
In the end, there’s not much to think about besides wrapping in Right
or Left
return values and map
ping whenever there’s a box and chain
ing whenever there’s a nested box.
Well, all of this is fun like building a boat in a bottle. But why the hell should I care?
Because of simplicity and composability. Functional programming is all about it: small, declarative and pure functions that are easy to read, test and compose.
Back to the functional universe
There are no try
s orif
s in the functional code.
That’s because they are treated as implementation details. It’s the Either
through Left
or Right
taking care of it for us.
Now we can dissect the other functions. First the ones directly used.
As you can see first
and parse
are the only functions doing some interesting stuff. But still no signs of if
s and try
s.
It’s only when we get to the library level functions that we see the ugly stuff. Luckily it’s hidden in the basement and we can totally forget about it.
As you can see,fromNullable
and tryCatch
just take care of boxing the outcome of their work into a Either
.
Altogether the functional code is as follows
Isn’t this a beauty? I haven’t been this excited since my Lego childhood.
Adding functionality
Now, what if we decided that we are passing an array of JSON strings and not just one string? And wait, let’s say the array contained integers and not strings.
This is all it takes. Still declarative, still well factored and we didn’t have to go and touch code any level deeper than that. None.
Outro
This is just the tip of the iceberg. There’s many other things like map
and chain
or Box
and Either
. That means we can slam dunk even harder!
Hungry for more? See how I refactor the functional code in the follow up post.
Pointers
Almost all of what I’ve covered here was stolen from a brighter mind than mine. So just do yourself a favour and
- read Professor Frisby’s Mostly Adequate Guide to Functional Programming;
- watch Professor Frisby Introduces Composable Functional JavaScript.
Thanks Brian Lonsdorf for showing me that FP is much more than using map
and reduce
on vanilla JS arrays. And that you can have fun bringing math into programming.
Get the latest content via email from me personally. Reply with your thoughts. Let’s learn from each other. Subscribe to my PinkLetter!