Monads for Java developers: Part 1 — The Optional monad
There is a talk about this article here: https://www.youtube.com/watch?v=vePeILeSv4E
abstract: Monads are simple yet powerful types that could give better structure to the code and in some cases can also help dealing with unwanted side effects. In this series of posts I will try to explain what monads are by showing some examples using Java, including a simplified version of the Result monad as an alternative to use Java exceptions for control flow.
What is a monad?
- Implements flatMap (a.k.a. bind) and unit (a.k.a. identity, return, Optional.of(), etc…).
- Follows three laws: Left identity, Right identity and associativity, which are out of the scope of this post.
For the sake of aligning the terminology, let’s say that a parameterised type such as Optional<String> has a type parameter: String and a wrapper type: Optional. In this case we say that “a String type is wrapped by an Optional context”.
How are monads useful?
Monads are all about composition of parametrised types. Let’s take the Optional monad as an example. We know that to be able to manipulate the parameter properly, we should at least check whether the optional is not empty and then get the value out of it. Also, it’s a good practice to do something in case the Optional is empty. We’ll call this entire process “unwrapping the parameter from the Optional context”.
Let’s write some code. First, let’s try adding two Optional numbers. If one of them is empty, then return empty, otherwise add them and return an Optional. Classic.
Notice how we had to manually “unwrap the Integer from the Optional context” by checking whether the numbers are present (line 2), and doing something with the empty case (line6).
This is precisely what monads helps us to do: To avoid dealing with the context when composing parametrised types. In the next example, the context refers to the Optional context, and composition refers to addition, so we can say: The Optional monad help us to avoid dealing with the Optional context when adding Optional types.
They do so thanks to the methods flatMap (a.k.a bind) and Optional.of (a.k.a unit). The flatMap method let us take the parameter from the monad and operate with it to produce another Monad of the same type. Because of this signature, we can nest flatMap calls on different monads to compose their parameters. Also, Optional.of (a.k.a unit) is a very handy method that will let us take any value and produce another Optional. Look at this:
1 — Bear with me with the weird indentation, it’s for didactical purposes. I will present an alternative later in this post.
2 — The flatMap method receives a Function. We’ll call this function the “mapper” function.
Notice how we didn’t need to check whether the values are present and deal with the empty case. The Optional monad via its flatMap method properly unwraps the parameter for us. It won’t invoke the second flatMap mapper function if the first Optional is empty. In fact, if any of the Optionals involved in the composition is empty, the result will be an empty Optional.
Also look how “val2.flatMap” (line 4) is nested inside the mapper function of “val1.flatMap” (line 3). This is because the main operation “first + second” (line 5) needed two parameters from two different Optionals. The mapper function of the nested Optional (val2) will have both parameters in scope.
Now, this is more than just syntax sugar (although it looks like it is because the Optional monad is a very simple one), this approach actually allow us to deal with the type parameter of monads without even needing to know how to unwrap it. We didn’t have to think what to do with the empty case, or how do we get the value out of the monad, or what would happen if we ever dare to get the value of an empty Optional. This is knowledge that optionalAdd method doesn’t need to have to be able to compose the monads. Such property becomes more important when unwrapping a monad parameter is not so simple. I will emphasise on this when we discuss the Result monad in the next post.
Meanwhile, let’s discuss another Optional example. We all know that nested Optionals can get a bit cumbersome to handle. Look at this:
Here we have in lines 4 and 5, two methods that return an Optional<Counter> monad and we want to calculate the total colourCount value of the two counters.
Look at the totalColourCount method (starting on line 8). To access the colourCount property of an Optional<Counter>, I need to unwrap Counter and then unwrap colourCount (line 13–19).
With flatMap and a bit of Java 8 syntax sugar, we can avoid handling the Optional context.
Here again the Optional monad helped us dealing with the context. This time, notice how we chained flatMap calls to unwrap the nested colour Optional property, and then we nested the other Optional colour (line 7) to be able to compose the monad (execute the operation on line 8).
Furthermore, fetchPreviousMonthCounts() (line 7) is only executed if fetchThisMonthCounts() (line 6) returns a non empty Optional. We have this nice feature for free thanks to the way the Optional monad implements composition via flatMap.
Wrapping it up
Monads are types that have a standard way of composition in which the context manipulation is handled by the Monad itself. It is standard because any parametrised type that satisfies the monad laws can be composed in the same way.
In Java, the Optional type follows all the monad laws and by nesting and chaining its flatMap method we can compose it nicely. Like Optional, Java provides other monadic types such as Stream and CompletableFuture. They behave in similar way to Optional in that they follow the monad laws and so, they are equally composable.
In the next post I will try to show more explicitly the impact of not having to deal with the context by discussing the Result monad. Also we will analyse how it can be used as an alternative to Java checked exceptions. We will then add side-effects-free logging capabilities to it and modify the flatMap method to be able to support composition of the log and comply with the monads laws.
ps: If you liked the article and believe that it might be interesting for someone else, please ❤.
An alternative to nested flatMaps for composing Optionals
If you prefer to avoid nesting flatMap calls when composing monads, there are utility functions that can do it for you. You can find plenty of libraries for that, however, implementing these functions is not complicated at all:
The flatMap2 function takes two Optionals and a BiFunction which in turn, receives the two Optional parameters and returns another Optional. Here, we just need to compose the Optionals as we have been doing it so far. With this function, the caller won’t need to nest the flatMaps when operating with the Optionals:
Using a similar implementation, we can have flatMap3, flatMap4 and so on. Note that this approach will preserve all of the benefits of Monadic composition that we have discussed in this post.
The full implementation can be found here and tests showing how to use it are in here.
 Optional is also known as Maybe in other languages such as Haskell.
 For a brief explanation of the monad laws, and also for a proof that Optional satisfies them, check out this gist: https://gist.github.com/ms-tg/7420496
 In this example, we could have used map instead of the second flatMap and save the use of Optional.of(). I did it this way to try to show the importance of flatMap and how can it be used for composing monads.
 http://www.javaslang.io/ contains a lot of utility functions not only for Monad composition but also for Functional Programming in general.
http://www.javaslang.io/ Excellent functional library for Java 8.
https://github.com/jasongoodwin/better-java-monads Great repository with Futures and Try monad in Java.
http://nazarii.bardiuk.com/java-monad/ Step by step monad coding example in Java 8.
http://learnyouahaskell.com/ Beginners book on Haskell and FP in general. The best out there IMHO, and its free! =)
https://medium.com/coding-with-clarity/understanding-the-optional-monad-in-java-8-e3000d85ffd2#.czvwugiz1 More Optional monad examples.