Instead of explaining the abstract theory behind monads, I’ll just show you some Java code that you already understand. And using this practical approach you’ll realize that you’ve in all likelihood already used monads a few times.
What are Monads?
Monads are just classes that implement the following two methods:
of
should simply take some values and return a wrapper for it (the monad). flatMap
then allows accessing those values by applying a function to all of them, taking the monads returned by those functions and combining (aka “flattening”) them into one again.
Using that, the simplest possible monad is the following:
public class Monad<T> {
private T value;
private Monad(T value) {
this.value = value;
}
public static <T> Monad<T> of(T value) {
return new Monad<>(value);
}
public<R> Monad<R> flatMap(Function<T, Monad<R>> f){
return f.apply(value);
}
}
So that’s it?
Mostly, but obviously you can’t just name the methods a certain way and have a monad. There are rules for how these two methods need to work together.
The good news is that if you implement those methods as described above (and as they are commonly understood), you’ll already fulfill all of them. But here they are anyways:
- The of method is a left-identity for
flatMap
, - the of method is a right-identity for
flatMap
and - the
flatMap
method is associative.
Or, in code:
// using
Monad<V> m;
V x;
Function<V, Monad<V>> f;
Function<V, Monad<V>> g;
// rule 1: of is left-identity of flatMap
of(x).flatMap(f)
.equals(f.apply(x))
// rule 2
m.flatMap(Monad::of)
.equals(m)
// rule 3
m.flatMap(f).flatMap(h)
.equals(m.flatMap(v -> g.apply(v).flatmap(h)))
Why should I care?
Now you know what monads are and how you can create one, but why is there even a name for it?
If you were using a heavily functional language, then this wouldn’t even really be a question since the tools and problems you’ll have when programming functionally will likely naturally result in you creating some monads¹. That’s where monads are from originally.
That being said, they can also be tremendously useful outside of those languages and in fact many new APIs feature monads.
Include (error) states in your domain
The best APIs try to stop you from making mistakes. One of the ways that this is done is explicitly including the error state in its return values. When multiple function calls in a row could return an error, this also has the added bonus of making the resulting code prettier.
Let’s look at an example using Optional:
public Optional<Integer> doSomething(Stream<Integer> stream) {
return stream
.findFirst()
.flatMap(this::integerHalf)
.flatMap(v -> Optional.of(v*5));
}
public Optional<Integer> integerHalf(Integer i) {
if (i%2 == 0) {
return Optional.of(i/2);
} else {
return Optional.empty();
}
}
In this example, we not only had the option to return an “error” at any time, but afterwards we don’t immediately have to check for it. Instead this can be done at a later point.
Monads can also be used to take care of non-error states. This is often used in collections or asynchronous programming (see CompletableFuture), where it also prevents callback-hell.
Allow the use of more generic functions
Take the above function integerHalf for example: We can use this on plain integers and also on any monad like Optional or Stream using the flatMap method.
This ease of access to the underlying information while not leaving the domain of the Monad can create amazing APIs that prevent many errors.
Do you know some monads?
I’ve already mentioned the three most common monads in Java:
-
Optional
, -
Stream
and CompletableFuture
but I’m sure you’ve encountered a few more already. Leave a comment if you know another good example.
Further links
- Maybe the best explanation of the theory behind monads (and functors and applicatives) — with pictures!
- ¹A great explanation of why monads naturally arise when programming functionally
- My 7-minute lightning talk about this topic
P.s.: The title image is a photo by Nicole De Khors and was chosen because monads are often described as boxes since they simply wrap some stuff.
Top comments (0)