as I have recently had the need to look for a new job, I had several interviews as Scala Senior engineer. There is still this weird habit to be very specific in the technical question, as id knowing Monads make you more senior or a better developer than not knowing them.
So I ended up searching for information about Monads, that was one of the recurring questions for which I had no idea or answer before starting the round of interviews. And yes, there is a reason for which I had no idea, and the reason is embodied in what is called “the Crockford’s law” that is basically a curse for which either you have never understood Monads or when you understand it you lose the ability to clearly explain them to the rest of the world.
For this reason I won’t try to explain Monads: I don’t need any curse, it is enough the bad luck that follows me. But I found a very interesting video that gives a nice and easy to understand overview of Monads. This video is in English, I am sure that youtube is full of explanations in other languages, but I found this one really simple to understand and I want to try to follow it in this post. First of all let’s share the video:
The explanation is a bit verbose because it explains lot of concepts that are really easy to remember for anyone who knows a bit of mathematics, so I would omit them here. The first important concept of the video is the one of function, nothing new. A function takes an argument of a type (for simplicity, in the first part of the video the type of the arguments is always a) and returns a value of a type (again, a).
The second important concept is the monoids: they are strictly bound to a concept of composition between functions. Let’s say that we have two functions:
f: a -> a
g: a -> a
We can compose them into a function h: a -> a by applying, to a variable x of type a, first g and the f to the result of g (so we compute f(g(x)) ) or the opposite, g(f(x)). In a general way the monoid is “a collection of things and a rule to combine them that, itself, should follow some rules” and in the video the example of monoid is the clock: it’s hours are combined by the formula (x+y)%12. The rules that the combining rule should follow in a monoid are 2: associativity (meaning that (x combined y) combined z = x combined (y combined z) and indeed (7+11%12)+8%12 = 7+(11+8%12)%12 ) and existence of unit element (meaning that there should exist an element of the collection u for which u combined x = x combined u = x, in the case of the clock this u is the value 12 for which (x+12)%12=(12+x)%12=x)
The step on category theory in which monads are better explained are only introduced in the video by saying that if we allow the previous functions f and g to have different types in input and output, we can compose them only in one way (so, if f: a -> b and g: b -> c we cannot compose them in f(g(x)) but only in g(f(x)) ).
Starting from the concept that functions, under compositions (with the same parameter type) are a monoid, we want to introduce compositions for functions that take an argument a but they transform it in a constructor that introduces something, calling it Ma. Ma is a construct that can be full of side effects (IO, exceptions, databases…). So we now have two functions:
f: a -> Ma
g: a -> Ma
and we want to combine them. We have to introduce an operator (>>= or bind in Huskell) that let us combine the two functions in such way that it takes a out of Ma from f and it push it in g to return another Ma. So the combination of the two is expressed as a third function, like before, that takes as input x of type a, converts it into an Ma and then, through >>= recompute it through g. In a more computer-like notation this is as following:
x => (Ma)(f(x)) >>= (Ma)(g(x))
where (Ma) is a cast for making explicit the type. This new function we can express it as h: a -> Ma but this composition is totally in charge of the bind operator, that should be designed. As long as Ma is the same for f and g we are in a monoid. But for making our Ma work as a monad, we have to design properly our bind operator to keep the associativity and the unit.
Let’s jump to Scala and let’s see how those concept are translated to this language. The unit concept is not straightforward: unit is a kind of wrapper for a value that doesn’t change the value. In computer science world the wrapper is what makes x of type a becoming of type Ma. If I can spoiler that the class Option is a monad, we need a unit that is not changing a but that wraps our construct Ma to add side effects or IO or, in this case… nothing! Easier like that, our unit is the constructor of our monad.
And what is our bind operator? It is an operator, a function, that, applied to a function f: a -> Mb and called on an object of type Ma returns an object of type Mb. Please note that this function does NOT force you to have only a as a type. But starting from a monad of type a it applies the function f to that and construct a monad of the same type but with elements in b. If you think to Option, or to List (also Lists are monads), which is the function that takes a List of elements, applies a function that for each of those elements creates another List of elements (eventually of different type), and it returns a List of the second type of elements? If you said map… NO WAY! It is the flatMap!!!
Please note that map can be defined in terms of flatMap and unit (constructor). Indeed:
m map g = flatMap(x => unit(g(x)))
and with this I think I said everything we have to say to introduce the monads in general and to give an idea of the mapping of monads in Scala.