Hello Folks!
So recently I got a chance to learn about Monads in javascript & I must say it was a hell of an experience. I spent some time learning the use cases & the exact definition of Monads in Javascript. Unfortunately, most internet posts are filled with Mathematical expressions & whatnot ๐ต.
But that was not it, there were some great articles too, which helped me in grasping the concept of Monads in Javascript.
In this series of blogs I'll try my best to walk you through the Monads basics & implementations, So stay tuned for the next part!
Now, without further ado, let's understand what a Monad is basically!
So what the heck is Monad anyways?
Monad is a design pattern used to describe computations as a series of steps. With monads, we can define a pipeline, a series of computational steps, that allow us to reuse more of our code, to write it in terms of highly composable parts. Ultimately, they are just boxes that wrap the value and obey a set of Monadic laws. They are extensively used in purely functional programming languages like Haskell to manage side effects but can also be used in multiparadigm languages to control complexity.
A rough explanation will look something like this.
Without Monad
f(g(h(i(j(k(value), j1), i2), h1, h2), g1, g2), f1, f2);
With Monad
identityMonad(value)
.bind(k)
.bind(j, j1, j2)
.bind(i, i2)
.bind(h, h1, h2)
.bind(g, g1, g2)
.bind(f, f1, f2);
Yeah, that was the basic definition of Monad in general, if we talk in terms of Functional Programming.
3 Laws of Monad
So as we discussed, Monads are just boxes with values that must obey a set of laws.
Now, what are those laws that define a Monad?
There are mainly 3 Monadic Laws, stated as Left Identity, Right Identity & Associativity.
1. Left Identity
If you have a box (monad) with a value in it and a function that takes the same type of value and returns the same type of box, then flat mapping it on the box or just simply applying it to the value should yield the same result.
Let's understand with the example.
let value = 1;
let promise = Promise.resolve(value);
let wrapInPromise = (x) => Promise.resolve(x * 2)
const boxResult = await promise.then(wrapInPromise);
const wrapperResult = await wrapInPromise(value);
console.log(boxResult == wrapperResult);
// Should log as true โ
// Thanks to top-level await ๐ค
I know! I know! Take your own time to wrap your head around it!
2. Right Identity
If you have a box (monad) with a value in it and you have a function that takes the same type of value and wraps it in the same kind of box untouched, then after flatMapping that function on your box should not change it.
Let's understand with the example.
let promise = Promise.resolve(1);
let wrapInPromise = (x) => Promise.resolve(x);
const boxResult = await promise.then(wrapInPromise);
const wrapperResult = await wrapInPromise(value);
console.log(boxResult == wrapperResult);
// Should log as true โ
Starting to make some sense now? ๐คซ
Let's see the last law then!!
3. Associativity
If you have a box (monad) and a chain of functions that operates on it as the previous two did, then it should not matter how you nest the flatMappings of those functions.
Let's understand with the example.
let promise = Promise.resolve(1)
let nestedPromise1 = (x) => Promise.resolve(x * 2)
let nestedPromise2 = (x) => Promise.resolve(x + 6)
const firstNestingResult = await promise.then(nestedPromise1).then(nestedPromise2);
const secondNestingResult = await promise.then((x) => nestedPromise1(x).then(nestedPromise2));
console.log(firstNestingResult == secondNestingResult);
// Should log as true โ
That was it, guys! I hope you guys had your 'Eureka !!!' moment in the end!
In the next part, we'll go on with the implementation of Monads in Javascript/typescript!!
Till then Sayonara !!! ๐