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?

  • 734