By Adam Recvlohe
A monad is a monoid of the category endofunctor 🤓.
A monad is a wrapper for a value which allows you to transform that value based on its wrapped state 🤔
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")
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.
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? 🤔
A natural transformation is transforming one monad into another (ex. Either to a Task). 🌳
Applicatives are wrapped functions that wrap monads that wrap values. This is called monadic inception 😂
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" }
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' ] }
Why use Monads?