Monad Pattern for Functional Programming with ES6
Introduction
There are times when, on the journey to enlightenment, one comes across a programming concept so mind-bending, and yet so simplistically elegant that they weep with inner joy. I mean sure, loops are great. But recursion is another beast entirely; allowing you to express yourself so tersely both in code and on paper that life without it seems dull, if not handicapped. Yet I, along with probably many others, struggled to apply recursion intuitively to solve problems when the concept was first introduced to me.
The Monad pattern is a similarly invigorating concept. One that requires a good bit of work to grok, but also adds a sophisticated construct to your arsenal. It can also help reduce moving parts in program, resulting in code that’s potentially easier to read. So let’s explore a bit to see what it’s about.
Programming with context
To really understand what monads are and why they’re useful, we need to take a short detour to explore contexts. Think of context as special powers attached to a variable which can be used in subtle ways as its value changes.
For example, imagine if our functions accept a value, knowing that there may not be a value at all. This is not exactly a new thing. We’ve been checking for null inputs since before dinosaurs.
However, what we’ve generally been doing was sprinkling if-else at random places to make sure nulls and invalid values are handled. This example is about abstracting this type of check to a higher level so that we may express our logic more clearly. In other words, providing a home for most boilerplate if-else checking.
Observe the following snippet:
class Maybe {
constructor (type, value) {
Object.assign(this, {type, value})
}
static just (val) {
return new Maybe('just', val);
}
static nothing () {
return new Maybe('nothing', null);
}
}
Maybe.just('Hello'); // Represents a concrete value
Maybe.nothing() // Represents an invalid
So Maybe is a class, while just and nothing are its two constructors. All concrete values can be thought of as wrapped inside a just while nothing represents nothing (literally!). We’ve used type to differentiate between the two. However, type is not the context. It’s just a representation of the idea that an object of Maybe can represent a value or absence of it. Take a moment to re-read the previous sentence again. When we write our code to deal with objects of Maybe, we’ll know that the object may actually be nothing.
If you found the above text hard to grok, just think of nothing as a glorified null for now. One which we define ourselves instead of (sometimes) incorrectly assuming null to always be invalid. just, therefore, is everything that’s not nothing. i.e. an actual value.
So, with our custom wrappers to represent valid and invalid values, let’s see how our lives have been made a little easier.
Say hello to chain a.k.a. the life-blood of the monad pattern
chain is a method which, wait for it, allows a chain of operations in sequence. What’s the rub? It also takes care of handling invalid or nothing values while it’s at it to make sure our code doesn’t crash and burn if users start sending nyan-cats through an email field.
We edit our Maybe class and add a chain method thus:
class Maybe {
// ... original definition of Maybe
chain (fn) {
if (this.type === 'nothing') {
return Maybe.nothing();
} else {
return fn(this.value);
}
}
}
Example Time!
We have a series of string manipulation methods, all of which need to check for certain invalid conditions to be a little robust.
const titleize = input => {
if (typeof input !== 'string' || input.length === 0) {
return '';
}
return input[0].toUpperCase() + input.slice(1);
}
const exclaim = input => {
if (typeof input !== 'string' || input.length === 0) {
return '';
}
return input + '!';
}
const someString = 'kent is superman';
exclaim(titleize(someString)); // 'Kent is superman!'
const someInvalid = 123;
exclaim(titleize(someInvalid)); // ''
Even for this small example, notice how adding type safety led to seemingly repetitive code. Let’s lift that up in chain method. Here’s the finished chain method with our custom logic ready for primetime.
class Maybe {
// ... original definition of Maybe
chain (fn) {
if (this.type === 'nothing') {
return Maybe.nothing();
} else if (typeof this.value !== 'string' || this.value.length === 0) {
return Maybe.nothing();
} else {
return fn(this.value);
}
}
}
const titleize = input => {
return Maybe.just(input[0].toUpperCase() + input.slice(1));
};
const exclaim = input => {
return Maybe.just(input + '!');
};
const someString = Maybe.just('kent is superman');
someString
.chain(titleize)
.chain(exclaim); // just 'Kent is superman!';
const someInvalid = Maybe.just(123);
someInvalid
.chain(titleize)
.chain(exclaim); // nothing
const someNothing = Maybe.nothing();
someNothing
.chain(titleize)
.chain(exclaim); // nothing
Observe how we removed the type and length checking and instead made the return value a Maybe object. Also, notice the pattern of execution went from function1(function2(input)) to input.chain(function1).chain(function2) which follows an arguably more natural flow. This might seem familiar to those who use promises to serialise async operations. They follow the monad pattern too, btw. If only javascript supported custom definitions for operators, our final code could’ve looked like input |> function1 |> function2. Nice, eh?
Wrap-Up
Note that the monad pattern is an abstraction like most other patterns, not a defined block of code. Case in point, what we saw above isn’t the high and low of it. It was a single example of how the monad pattern can be applied to deal with invalids. There’s lots more ways to exploit monads for a ridiculous variety of tasks. Feel free to google Either Monad, Logger Monad or Parser Monad to explore a few more.
Hopefully this post gave you insight to leverage the Monad pattern. I’d be happy to read about you using it in your work or personal projects. Share your thoughts in the comments below. Use it to direct any hate, praises, WTFs too :)
Note to reader:
- The chain is actually called bind in monadic nomenclature. But since the word is already in javascript’s dictionary for an entirely different purpose than what we used chain for, it was rechristened.
- What we saw above is monad the pattern. A Monad (the thing) is a functional typeclass which lists a few other helper methods in addition to bind (or chain from our code). It works similarly though.