DEV Community

Cover image for You already know Monads
Oscar Ablinger
Oscar Ablinger

Posted on • Originally published at ablingeroscar.Medium

You already know Monads

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.

Oh and if you prefer to listen to me instead, this post is based on a lightning talk I gave, where I (try to) explain them in 7 minutes.

What are Monads?

Monads are just classes that implement the following two methods:

  • of (in explanations usually called unit or return) and
  • flatMap (usually called bind)

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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)))
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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

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.

Discussion (0)