So far we’ve looked at monoids and functors. The next algebraic data structure we’ll cover is a monad. If you’ve wondered what a monad is but never really understood it, this is the post for you. I am sure that you’ve used it without realizing it. So let’s get to it.
Definition
A monad has more structure than a functor. This means that you can call map
on it and that it obeys all the functor laws. In addition, a monad has a flatMap
function which you can use to chain monads together. In essence, monads represent units of computation that you can chain together and the result of this chaining is also a monad.
Let’s look at a few examples.
Example
1 | val first = List(1, 2) |
The above code[1] uses a for
comprehension to muliply elements of the list together. Under the hood, this gets translated to:
1 | first flatMap { |
The compiler is making use of the List
monad to chain operations together. Let’s break this down.
1 | next map { |
This part of the code will return a List
since that is what calling map
on a List
does. Since we have two elements in first
list, the result of mapping will generate two lists of two elements each. This isn’t what we want. We want a single list that combines the results together.
1 | first flatMap { |
The flattening of results is what flatMap
does - it takes the two lists and squishes them into one.
Monad Laws
For something to be a monad, it has to obey the monadic laws. There’s three monad laws:
- Left identity
- Right identity
- Associativity
Left Identity
This law means that if we take a value, put it into a monad, and then flatMap
it with a function f
, that’s the same as simply applying the function f
to the original value. Let’s see this in code:
1 | scala> def f(x: Int): List[Int] = { List(x * 2) } |
Right Identity
This law means that if we take a monad, flatMap
it, and within that flatMap
we try to create a monad out of it, then that’s the same as original monad. Let’s see this in code:
1 | // right identity |
Let’s walkthrough this. The function to flatMap
gets the elements of the original list, List(1, 2, 3)
, one-by-one. The result is List(List(1), List(2), List(3))
. This is then flattened to create List(1, 2, 3)
, which is the original list.
Associativity
This law states that if we apply a chain of functions to our monad, that’s the same as the composition of all the functions. Let’s see this in code:
1 | scala> def f(x: Int): List[Int] = { List(x + 1) } |
Conclusion
This brings us to the end of the post on monads and their laws. List
isn’t the only monad in your arsenal. Option
s and Future
s are monads, too. I suggest going ahead and constructing examples for monadic laws for them.