Monads, here and back again
The goal of this blog post is to show what monads are and how they can be used in functional programming. As a tool for this demonstration Scala programming language will be used.What about the name? Well it comes from mathematics, category theory to be precise. And as my university professor used to say mathematicians like to show off and thus like to use strange names for things, so they sound scary to outsiders.Monads found their use in functional programming. So what are they in the programming context? Simply put, they are type wrappers that satisfy certain conditions and enable us to chain expressions and method calls easily one after another. But wait, we can do this without any formalized structure. We just need to create class with methods that has a this as a return value. Using this we can chain method call without any trouble right ? Well yes and no. What if we need new feature/method in our class to be used with the existing ones. In most cases we would need to add new method to class and implement it accordingly. But you will agree that this is not compliant to the Open/Close principle which simply states: “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. So let’s think how can we support new demands and still practice SOLID principles.
The solution is found in the functional programming languages in which functions are first class citizens, which means you can do with the function everything you can do with an ordinary variable. You can pass function to method as arguments or return it as a value of a method. Now we can define a function that will support our new feature and pass it to the method of the class instance. In this way we somewhat generalized this problem and we didn’t break Open/Close principle.
In a more formalized fashion Monad is generic that encapsulates values of a particular data type. Computations of this new data type follow laws defined by the following interface/trait.
As seen from the previous code snippet function
apply is used to create monad from some arbitrary type
A (it is not part of the trait). The result of this operation will be instance of
M[A]. On the other hand
flatMap method as a parameter takes function
f which converts wrapped type
A into the monad
M[B]. The overall result of
flatMap operation is new monad
Since this can be confusing at first we will explain it on list example in Scala. Class
List is in fact a monad. But before we jump into the explanation of
flatMap method we will shortly describe
map method just unpacks monad, transforms inner type and packs new type back into the original type.
In case of a list,
map method as a parameter receives function that will transform every element of a list. So imagine that we have a
List[Int] and we want to create a new list that will contain all elements of the original list multiplied with itself. The result will be again the
map method the final result can be
List[String] or list of any other type, all depending of the return type of a transformation function.
OK now that we understand what
map method can do let’s continue with
flatMap method. The easiest way to explain
flatMap method is to construct it with the
flatten methods. Look at the following illustration:
Everything starts from the monad
M[A] and we call
map function that transform type
A into a monad
M[B] and it packs it into the starting monad. So we have
M[M[B]]. After this we need to flat the hierarchy so
flatten method is called. As you can see end result is
M[B], again a monad, so we started from one and finished with another monad, and that is the motivation for the blog title :).
Just to be sure that we are on the same page, we will look at the
List example. Using
map function every integer is transformed into a new list that contains all even numbers between zero and current element. List is flattened so we get only one final list. We can see that the
flattenList is identical to
Since Scala doesn’t have monad as a built in type, we will cover several classes that are implemented as a monads in Scala:
Do I really need to check for null value ?
If you are a software developer, there is a great chance that you run into
null value. This special value indicates that your variable is not referencing any of the objects stored on the Heap. Calling any operation on that kind of a reference is causing
NullReferenceException. Therefore if we are not really sure how some object is created we need to check it against
null and prevent runtime exception to be raised.
In Scala there is a type wrapper, a Monad to be precise called
Option[T] which can significantly improve the ways of handling
Option[T] has two subclasses
Some[T], representing null and non-null values respectively.
Let’s look at the example and see how can we chain multiple methods onto some initial
Book represents one book in the library information system with its attributes. Notice that genre is not necessarily populated, so we wrapped it in an
Option. On the other hand we have
BookRepository which holds all books in memory and has method for getting book by
isbn. Since passed
isbn doesn’t have to be in the repository return value of that method is
Option[Book]. Let’s see this in action:
In this oneliner we requested the book with
isbn equals to “ISBN-1” and after that we run
flatMap and got
Option[Book]. Then we run
map in order to format the result. Notice the
getOrElse method which will be run if any of the method along the way yields
None. So if
None is returned from
getByIsbn method, execution of
map execution will be skipped. I would say that this is one safe and elegant way of handling
nulls in our applications.
What if I told you that time travel is possible and that it can be done without entering our closest black hole :). Scala is equipped with very powerful monad called
Future[T] to be precise. Type
T represents type of the expression which is wrapped in the
Future. This expression represents calculation that will start at some nondeterministic time after that
Future instance has been created, by some thread assigned to it by the
ExecutionContext. You can imagine
ExecutionContext as a Thread pool. So why this monad is so interesting? It facilitates asynchronous programming and help us to use system resources in an optimal way. Instead of blocking current thread while communicating with a database for example, we will store thread’s context and put thread back to the pool. When the requested data is ready, a thread will be picked up from the pool together with the corresponding context (note that it can be a separate thread) and execution can be continued. While we were waiting for database response our thread could be used to serve some new request. OK, but how this has anything to do with the time travel? Scala enables us to chain one action after another. More accurately, we can define what action will be called in the future, after the first is done. This is why the class name is
Let’s look an example of this:
As you can see we are creating
Future and inside of it we putting thread to sleep (which runs the future, separate from the main thread) for random number of milliseconds. Then a new random number is generated and depending on the value we are returning number generated or throwing an Exception.
Later on we have
onFailure callback registration. Here we’re printing the result of
Future execution. While all of this is happening main thread continues to run. If you run the code above, you can get different results which is expected.
What about the chaining of
Futures? Well this is possible and we just need to use now already known
map methods. Let’s look at the simple example:
Here we are creating
fut2, since they are vals they will be started almost immediately after declaration. Then we are defining
fut3 by calling
fut1. As we now return value of this
flatMap must be
Future. Inside we are unpacking
fut1 and use
num1 as an input parameter for creation of the new
Future. And finally new
Future will be created by calling
map as we know just unpacks monad, do some transformation, and packs it back. Transformation is one sleep of 2000 milliseconds and sum of
num2. So when the inner
map is done we have return value for an outer
flatMap. The end result is
fut3. We could also define
fut3 as methods (
def) and in that case they will be called right one after another in the same example. One more thing, you should call await on the future only in you test classes. We are using it here just to show some results.
For details how to recover from a future in case of exception or to make some side effect please check following link.
Try monad wraps a computation that could either result in a value or in an exception being thrown. Subtypes of
This simplifies exception handling and propagation. But let’s check one more straightforward example:
As it can be seen, we are wrapping conversion of string to int in a
Try block. When the
map is called,
Try is opened, value is taken, transformed (squared) and returned back to the
Try block. Then, again the same thing using
map value is divided by 2. Variable result now holds
Later on we are using pattern matching to extract information from
Try block, integer result if everything was successful or message from the exception. Using pattern matching we have ability to incorporate different actions for different types of exception.
In case that we had some other string that can not be converted to int, two successive
maps would not be called at all.
Again I would say this is more elegant approach than using traditional
catch blocks and passing information about exceptions using re-throwing.
And that’s all folks. Monads in Scala functional programming language. Scala doesn’t have build in Monad type, but instead there are many classes as we saw that were implemented in this way. But if you like what you seen here, you can check and try libraries like Scalaz or Cats. In this blog post we didn’t cover for comprehension, which is just a syntactic sugar for
flatMap methods. We urge you to check it since it can make your code much more readable and maintainable. Your comments, suggestions and critics are more than welcome.