The Monad Interface

Monad Explained Simply

Oct 24 · 7 min read
Image for post
Image for post
Photo by Michael Dziedzic on Unsplash

Monads are all-the-rage these days in the JS world. I’d be surprised if any serious JS developer hadn’t heard of Monads. But just in case you haven’t, I like to describe a monad as a fancy box. It’s a special box. You can’t (well, you shouldn’t be able to) see exactly what’s inside the box, but you can know that this box holds values of a certain type, and exposes three primary operators with which we can modify or take out the content of that box. Those operations are what I call the Monad Interface.

OK, so Monad isn’t exactly an interface. But in JS we can think of it as one. As a quick refresher, in a strongly typed language (such as the C family) an interface is the definition of what a class object has. If you’ve ever used TypeScript, you know that an interface describes the expected shape of something — like an object, or the parameters and return value of a function. What an interface boils down to is a contract to be upheld by anything that implements that interface, but it gives no specifics of the implementation. It’s useful to think of things as interfaces, because interfaces are abstract. Abstraction is one of the most useful concepts in computer science. To say something is abstract is to express how far away it is from machine code. The further away from machine code we can be the better because humans brains understand the abstraction of languages; only machines are made to read machine code. The best part about an interface is that it is a cost-free abstraction. If you write one in TypeScript, for example, it doesn’t even generate code in the end, it only compile-time checks that the interface’s contract is upheld. In bare JS, an interface is only in the mind of the author. So we can only uphold an interface’s contract by assuring on our own that all of the expected methods and fields are correctly implemented — there are no compile time checks to help you out. But that’s OK, because the dynamically-typed nature of JS comes with some other very nice benefits, which I won’t necessarily discuss just this moment.

So without further ado, here is the interface that Monad adheres to (in TS interface notation):

Image for post
Image for post
The Monad Interface in TS notation.

It’s actually not that complicated. I’ll go through each of these three functions and make them crystal clear.

First we’ll start with map. map is the easiest function to grasp of the Monad interface. We use it all the time in JS. It states, ‘Given a monad M a and a function a -> b, apply that function to the contents of monad M a producing a monad M b. You see map in the wild in native JS’s Array type. In Array, map simply applies the provided function to each element in the array, producing a new array:

Image for post
Image for post
An example of the map function as used by Array.

Notice the way I call map with just the function sq's identifier — that’s a declarative style of coding. You define things in one place and call them by name elsewhere. It actually makes things much less complicated to understand to call them simply by name, because it de-clutters the code you write. Plus, you might want to call sq in more than one place. Why write out the anonymous function x => x * x every time you want to call sq? Also see how nice that is to read? It’s nearly plain English, ‘Apply function sq to the contents of a1 and assign that to a new array a2’.

Now for unwrap. unwrap is the most pointless-seeming of the Monad interface. You may know it as flat, flatten, join, emit, and probably others, but all it means is, ‘Take the contents out of the box’. Although it may seem like a silly thing to have, it is actually a really important function. If you couldn’t take the inner content out of the box, Monads would be pretty useless. That being said, you shouldn’t normally be calling unwrap, and instead should opt for a function that considers every variant of your Monad.

Consider this snippet, defining a minimal Option (a monad that handles nullable values gracefully).

Image for post
Image for post
The Option Monad with an example of correct unwrapping.

That snippet creates the basis defines a simple container with two variants (Some and None) called Option. It isn’t a full fledged Monad yet, because we haven’t defined map, chain, or unwrap on it. Notice the definition of or on the Option namespace-object. It calls unwrap internally instead of directly. It checks for each variant, throwing an error if it isn’t a variant of Option. This is preferential to unwrap because unwrapping a None value will simply return a None. Most Monads have a specific function for unwrapping them safely, considering null or undefined values and responding appropriately and, more importantly, predictably.

Let’s flesh out Option with map and unwrap operators as we defined above.

Image for post
Image for post
Map and Unwrap implemented for the option variants Some and None.

Now we’ve got some real functionality. Let’s quickly discuss what this means:

  • Option has two variants — Some and None.
  • You can either have Some of something, or nothing at all.
  • When you have Some of something, you can map and unwrap it just like normal.
  • When you have None of something you can try to map or unwrap but you still just have None, so that’s why we keep returning it.

Now we can map functions over the contents of our box. If we stopped at just map, we’d have what is referred to as a Functor. A Functor is a fancy box that can map. It’s easy to mistake a Functor for a full-fledged Monad, but they are different. There are mathematical Identity laws that must be upheld to properly define a a construct as a Monad, involving both the unwrap and chain methods. We won’t exactly get into all of that, but we will define chain, which when defined properly along with unwrap pretty much does make a Functor a Monad. Let’s define chain now:

Image for post
Image for post
Chain of the Monad Interface implemented on Some and None.

See why I saved chain for last? It’s given to us for free by defining map and unwrap on a type. chain is really for chaining together functions that lift values into Monads, discarding the outer Monad by unwrapping. It’s a kind of mathematical symmetry that allows us to apply monad-constructing operations without getting a box-in-a-box-in-a-box kind of situation. Those are the types of complexities FP aims to avoid.

Now we can play with chain a bit:

Image for post
Image for post
A simple use case for chain of the Monad Interface.

If we didn’t have chain, we could still map functions like this. The problem with that approach is that then we end up with an Option(Option(A)) instead of just an Option(A). It’s much more complicated to work with boxes in boxes. Let’s just look at what would happen:

Mapping an Option-returning function into an Option.

See how we now have to consider what our nested values are, and then add more and more unwrapping as we go? That’s why chain is so very important — it makes an otherwise fairly verbose series of operations very simple and easy to understand. One can only process so much information at a time, so why not make life better for yourself by making the that information more expressive? It just makes sense. Imagine trying to process some retrieved JSON using a method like prop that lifts the result into a new monad — every nested value creates a new Monadic wrapper if we don’t use chain to unwrap each result. That would result in unwraps equal to the level of nesting you need to descend — not ideal. Moral of the story: use chain to safely operate on your value while shedding the excess containers.

So now we know how to define simple Monads based on this interface. Here’s a pen that will let you hack at the Option Monad:

Hope you enjoyed an adventure in Functional Programming. Until next time, FP on folks!

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store