• Post Reply Bookmark Topic Watch Topic
  • New Topic

Monad in FP & Samples from the book  RSS feed

Ludovik Lax
Posts: 17
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Pierre Yves,

Wikipedia states a monad "is a structure that represents computations defined as sequences of steps: a type with a monad structure defines what it means to chain operations, or nest functions of that type together. "
Does the book covers this concept in details with clarity and samples ? How monads can be designed and implemented in Java 8 ?

Many thanks

Pierre-Yves Saumont
Ranch Foreman
Posts: 156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Ludovik,

The book has not a specific chapter about monads, although there are many monads built and used along the book. Up to now (I am now writing chapter 13).

One reason is that there is an old saying about monads having a kind of magic: as soon as you understand them, you loose the ability to explain them to others.

However, this could be a good place to test my ability to explain the concept.

Searching on internet, one may find many other definitions such as:

- "A monad is just a monoid in the category of endofunctors"

- "A monad is a computational context for some value"

- "A monad is a class with a unit and a flatmap method"

You may also find some more exotic definitions such as:

- "Monads are buritos"

- "Monads are elephants"

The first three definitio are valid... in some context. The first one is probably the most rigourous definition in the context of Category Theory, a branch of maths about which most programmers don't care.

The third definition is probably the most easily understandable by Java programmers. But of course, the names of the methods are unimportant. What matters are the rules these methods must respect.

The second definition is probably the most useful to understand monads. So, monads are computational contexts. Functional programming is programming with functions. Safe functional programming is programming with total functions. Functions that are not total are said to be partial. This means that they do not always have a value. And when they have no value, they are not happy and start doing awful things. And they stop being pure.

Take a function like f(x, y) = x * y. Is this a pure function? No one can say. It depends on the context. In some languages, it could throw an arithmetic overflow exception. So it would not be a total function since it would not be defined for all pairs (x, y). As a consequence, it would not be a pure function because throwing an exception is a side effect.

In the context of Java with integers, this function is pure. This means that whatever couple of integer you give to the function, it will always return a value, and always the same value for the same pair. So, you can trust the function. This does not mean the result will always be correct. In case of overflow, the result might not be what you intend, but this is another problem. There will always be a result (meaning the program will not hang in the wild) and this result will always be the same.

What about this function: g(x, y) = x / y

Considering Java and integers, which means a function taking a pair of integers as its argument and returning an integer, this is not a total function. It might have no result for some pairs of integers. If the second argument is 0, there is no result and the function throws an exception. This is because g is not a total function if considered as a function from (integer, integer) to integer.

There are two ways to make g a total function: change the domain, making it a function of (integer, non null integer), or change the codomain, making it a function of (integer, integer) to (integer | exception).

To implement the first option, we would have to create a new type: NonNullInteger. This is perfectly possible.

To implement the second option, we would also have to create a new type: IntegerOrException.

Functional programmers prefer the second approach.

But wait, if we change function g to return IntegerOrException, we can no longer compose it whit f. More precisely, f . g (x), or f(g(x)) if you prefer this notation, will no longer compile because the types no longer match.

The solution is to create a computational context in which the functions can be safely executed. If you like metaphores, you can think about the context as a safe box.

So, what you need is:

- a safe box

- a way to put the parameter value inside the box

- a way to put the modified function inside the box so that it can be applied to the parameter value.

And that's it. The result will be a box containing the result of the function.

To take a simple example in Java, we have to slightly modify the requirements, because Java does not offer such a safe box. It offers three types of safe boxes, but not one suitable for this use case. So, we will modify the requirement by saying that in case of error, the result will not be throwing an exception but simply nothing. (Note that Java as a type for this: Void. However, instantiating this type is a bit tricky.)

The type of safe box we may use for returning a result or nothing is Optional.

The method to put a value in the box should have been named unit, but it was named of. It does not change anything.

The method allowing to apply the safe operation to the value inside the box is called flatMap. Let's take the example of a simpler function taking a String and returning the first character. A "normal" method could be:

To make this function return either the first character or nothing, we must change it into:

To use these tools, we need to put a String in context:

unit (of) and flatMap are all what is needed to make Optional a monad. There are however other use cases that are frequent enough to have been added in most monads implementations. For example, you might have to use a function returning a raw value, such as:

You could do it with:

But this is not very efficient since the function will wrap the result in an Optional, just for the flatMap method to unwrap it. So, there is a map method for this use case:

Optional<Integer> upperChar = character.map(ThisClass::toUpper);

Note that if you were to use map with a function returning an Optional, such as:

you would obtain an Optional<Optional<Character>>. This can be changed into an Optional<Character> using a method called flatten, but this method is missing in Optional. However, as you can see, there is a strong relation between unit (of), flatMap, map and flatten. flatten can be implemented as:

If you wonder what this identity method is, you are realizing that we were just hit by a Java generic problem: the absence of parameterized properties. In Java, you can declare parameterized classes and methods, you can parameterize methods calls, but you cannot declare parameterized properties if the parameter is not already in scope. If you look at the implementation for Function.identity(), you will see that this is a trick:

This is a method returning a function. It should have been declared as a property:

but this does not compile.

Lots of other use cases may be abstracted inside monads, although they are not necessary to make a type a monad. One of the most often used is fold. This method is generally seen as specific to vectorial types, such as List or Stream, but it is not. If Optional was the parent class of two sub classes, say None and Some, fold could be implemented in None<T> as:

and in Some<T> as:

(Thechnically, this is a left fold, but the difference is irrelevant for this example.)

As the Optional class is final, we can't add this method. We can write an external implementation:

But we are using Optional.get(), which is awfully bad. (It is forbidden to access the value from outside of the context, which means this method should not be public.) There is no smart solution to this problem. We know that the get method will never be called if the value is not present, so we could write:

But this is ugly. The less ugly implementation would probably be:

Anyway, folding an Optional is useless, except to understand that the orElse method is in fact a fold, and could be defined as:

Yes again, this is totally useless, but it becomes useful to understand it when studying other monads such as Stream (or List, but not the java.util.List)

Lots of other methods could have been added to Optional and are missing, and you can't add them since Optional is final. This is a good reason to develop a totally new Option monad. But at the same time, Optional is almost useless because it can't carry the reason for the absence of data. This is why an other monad is needed. In the book, I called it Result. It roughly corresponds to the Scala Try class.
Enrique M. Talavera
Posts: 25
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Wow, amazing explanation,Mr.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!