In the company that I work for, we use a lot of functional code to solve business problems in a more natural and safe way. And mostly of that code are written in Java.
In purely functional programming languages such Haskell, there is monads as a native language structure, but in OOP/FP languages such as Java and Scala we also can create and work with monads in an almost natural way :P.
Just to contextualize, a monad is a kind of “wrapper” that can represent or perform computational sequences on a data structure wrapped by the monad, easy to understood, right? And also, a Monad must follows a set of laws, simply called monad laws:
– Left Identity
– Right Identity
All it takes be a Monad is to provide two functions which conform to three laws:
The two functions in some langs examples:
Place a value into monadic context:
– Haskell’s Maybe: return / Just
– Scala’s Option: Some
– Java 8 Optional: Optional.of
Apply a function in monadic context:
– Haskell’s Maybe: >>= (aka bind)
– Scala’s Option: flatMap
– Java 8 Optional: flatMap
Well, talking about Java 8, we have a monad called Optional.
A lot of programmers just uses Optional to prevent NullPointerException, because with Optional, you can verify the object consistence before performing some computational operation. For example:
// The simplest world's example Optional.of(Arrays.asList(1,2,3,null,5,6)) .map(i -> i.get(3)) .orElse(4);
In this boring example, we just wrap a immutable List of Integer on a Optional and try to get the nullable value from 3 position. The Optional helped us allowed the T orElse(T t) operation ir order to prevent gets a null value. But the Optional class can be helpful for a lot of other complex computational sequences.
So, let’s se how JDK8 Optional apply the three monad laws:
Left Identity: If we put a value in the monadic context and bind a Java Function to it, its the same as just applying the Function to a value:
Function<Integer, Optional> addOne = x -> Optional.of(x + 1); Optional.of(5).flatMap(addOne).equals(addOne.apply(5));
Right Identity: If we have a monad and bind that monad’s return method — it is the same as the original wrapped value:
Associativity: If we have a sequence of functions applied to a monad it doesn’t matter how they’re nested:
Function<Integer, Optional> addOne = i -> Optional.of(i + 1); Function<Integer, Optional> addTwo = i -> Optional.of(i + 2); Function<Integer, Optional> addThree = i -> addOne.apply(i). flatMap(addTwo); Optional.of(5).flatMap(addOne).flatMap(addTwo).equals(Optional.of(5). flatMap(addThree));
this is so fashion, is not it?