What is a Monad?
By Adam Recvlohe
A monad is a monoid of the category endofunctor 🤓.
In my own words
A monad is a wrapper for a value which allows you to transform that value based on its wrapped state 🤔
Example: Maybe
The Maybe monad has two types, Just and Nothing. Meaning, it will either return type of Just and its value or it will return type of Nothing.
Call me maybe 📞
-- Kylie Rae Jepsen Monad
But we already have a Maybe type in JavaScript!
It's the endless staircase of hell 😈
or short-circuit evaluation
var array = [1, 2, 3, 4, 5]
var value = array &&
array[42] &&
array[42] * 42
Maybe with no staircase
const array = [1, 2, 3, 4, 5]
const value = Maybe
// .fromNullable(array)
// .chain(arr => Maybe.fromNullable(arr[42]))
.fromNullable(array[42])
.map(v => v * 42)
.getOrElse("Nothing to see here, move along")
Example: Either
The Either monad has two types, Right and Left. Right is passed if there is a success and a left is passed if there is a failure.
Simple Example
const R = require('ramda');
const Either = require('data.either');
const object = {
deeply: {
nested: {
object: {
"with": {
some: {
value: 42
}
}
}
}
}
}
const value = R.path(['deeply', 'nested', 'object', 'with', 'some', 'a'], object)
const hasValue = Either.fromNullable(value)
hasValue.map(v => v * 42).getOrElse("Oh nos, we don't have a value")
But we already have error handling, it's called the .catch, try/catch, and callback(err, res)
But can you handle errors with natural transformations? 🤔
Natural Transformations
A natural transformation is transforming one monad into another (ex. Either to a Task). 🌳
A not so trivial example
Yeah, yeah, what else you got?
I gots Applicatives
Applicatives in my own words
Applicatives are wrapped functions that wrap monads that wrap values. This is called monadic inception 😂
Applicative Example
var user = {
name: "Adam",
username: "adam",
email: "email"
};
const createUser = lambda.curry(2, (email, username) => ({
email,
username
}));
function checkEmail(user) {
const email = user["email"];
return new Task((rej, res) => {
setTimeout(() => {
res(email);
}, 3000);
});
}
function checkUsername(user) {
const username = user["username"];
return new Task((rej, res) => {
setTimeout(() => {
res(username);
}, 500);
});
}
var res = monads.liftMN(createUser, [checkEmail(user), checkUsername(user)]);
res.fork(console.error, console.log); //= { username: "Adam", email: "email" }
Parallelism
If you want to achieve parallelism use the Fluture library
const Future = require("fluture");
const c = Future.reject("c").fold(Either.Left, Either.Right);
const d = Future.of("d").fold(Either.Left, Either.Right);
Future.parallel(Infinity, [c, d]).fork(
() => {}, // noop
ms => {
const values = ms.reduce(
(acc, m) => {
m.cata({
Left: v => acc.errors.push(v),
Right: v => acc.results.push(v)
});
return acc;
},
{ errors: [], results: [] }
);
console.log(values);
}
);
// { errors: [ 'c' ], results: [ 'd' ] }
Conclusions
Why use Monads?
- Safely handles pointer exceptions in a declarative way
- Allows for more expressive transformations that are composable
- Side-effects are handled outside of the logic of your function
- Dynamic computations can be achieved with the help of applicatives
- Easier (?) to reason about your application
Recommendations
- Watch/read anything related to Professor Frisby, aka DrBoolean aka Brian Londsdorf. He has a video series on this on egghead.io
- Use a library like Folktale for JS projects.
- Start out small, using the Maybe monad
- FP in JS is hard and I consider it somewhat of an anti-pattern. Switch to Elm/Haskell/Elixir if you don't want to deal with dependency hell 🔥
That's all folks 🐷
What is a Monad?
By Adam Recvlohe
What is a Monad?
What is a what?
- 848