/home/rawley.xyz

Monads are not hard

When most people hear about monads they often shudder, but in reality they‘re a fairly simple concept. For some reason however, ivory tower jokers like to confuse people to fuel their ego, making people think that monads are some sort of false deity. In reality, a monad is just a wrapper. You can think of it as a box, container, wrapping paper, whatever. The key thing is, we know what type of things can be inside this box, but we don‘t know exactly what is inside. A good code example is the List type. When we look at a type definition for a List, if your language is statically typed, you‘ll know that it is a List of type whatever. If you‘re a Java developer a list typically looks like:

List<T> // Where T is the type of things that this list CAN contain.x

Now, the main two states a list can be in is 1, the list is empty, or 2, the list is not empty. If we want to do anything with this list, we have to figure out first, what state is this list in? This process is called Bind (you‘ll often see it expressed as >>=), now we enter the world of functional programming. Since we‘re functional programmers now, let‘s setup two small facts, functions, just like data may be passed to other functions, this concept is known as first-class functions. Lastly, you may express 2 functions that compose together as one function. For example, f(g(x)) can be considered h(x), in JavaScript (sorry Java makes this way too hard), that would look like this (this is called monoid):

// Assume f and g are 2 arbitrary functions that return the same type that they produce
const h = (x) => f( g(x) );

Now that we have our basic facts about functions, it‘s really simple to use them to perform operations on Lists! First we have to quickly determine the type signature of Bind in terms of our list. Bind is really simple, it‘s a function that has 2 parameters, the first being our Monad (in our case a List), and the second being a function to apply to the contents of that Monad (remember a Monad can be considered any container). Bind is defined per monad, a typical bind for a List might look like:

// Where func is of type: T -> List it takes a value stored in a list, and returns a new List with that element changed
const bind = (monad, func) => if (monad.length = 0) { return monad } else { return monad.flatMap(func) }

// Now lets use it
const myListMonad = [1, 2, 3];
const myNewMonad = bind(myListMonad, val => [val*val]); // [1, 4, 9]
// Here is the generic type signature of Bind: Monad<T> -> (T -> Monad<U>) -> Monad<U>

Congratulations, you‘ve just used a monad! Now, Bind is not the only operation a monad can perform, typically you‘ll see a function called Map, which has a similar type to bind except the function maps from T to U rather than T to Monad U, and another simple function called Return, this function is very important in the functional world but in the world of non-functional languages it‘s not too important. Return simply takes a T and returns a Monad T. In the case of Lists, return would take an element and return a new list with that element inside of the list. At this point if you have some programming experience when you hear Map, you should think, oh, I know that one! Yeah, every List type in almost every language that I know of has a List.Map of some sort. Guess what, that‘s an operation on a Monad!

Before I wrap this up, if you‘re still confused it could be helpful to think about a Monad that I can (almost) guarantee you‘ve used before (if you‘ve written any JavaScript within the last 5 years). Promise is a monad! When you call .then on a Promise you might as well be calling .map. This is because .then will return a new promise with the return type of the function that you passed to .then; pretty neat right! You‘ve been using monads this whole time without even knowing! Another less know function on Promises is... you guessed it, .bind! You can use this to functionally hide the possiblity that the promise has not resolved yet, just like map, but instead, you define the promise to be returned rather than it mapping your return to a promise. The new JavaScript await keyword is also, drumroll please... map! It can only be used within an async function (the realm of the Promise monad)!

So. Why do I need monads? Well, without them, all side effect producing code could not be possible within a functional language. When I bind a monad, it might be calling my database, it might be doing IO, it might be logging, etc. We use monads to abstract away the impure code, so we, as the developer can focus on code that is rigorous and well-defined. If you‘re interested in learning more about Monads, it would really help to learn a language that prefers them over other, more imperative approaches to side effects. Good languages for this are: Haskell, OCaml, and Standard ML. These languages can seem a little hard to approach, but should you perservere, an exciting world of functional growth awaits!

Quote of the Week: The State calls its own violence, law; but that of the individual, crime. -- Max Stirner