In functional programming monad is a design pattern which is used to express how states of computations are changing. It can take a form of some abstract data type constructor with two abstract functions.
In scala
we can define this contract using Monad
type class
1 2 3 4 5 6 7 8 9 |
|
Functions pure
and flatMap
for a given monad M[_]
have to follow
some laws - I will talk about them later.
Function map
can be defined
in terms of flatMap
and pure
and this is a bonus which we get for a free
when we provide an instance of a Monad for a type M[_]
. Moreover many useful
functions can be defined in terms of flatMap
and pure
like map2
, ap
,
filter
and so on. This interface is so powerful then often is treated
as a primitive one when goes to implement other functions.
We can think about M[A]
like about some smart container
for a value (values) of type A
. This container abstracts away from how this value
is kept. We can have many flavors of them like container:
- aware of whether or not the value exists
- with more then one value
- for which getting the value would trigger some kind of
IO
operation - with value which eventually could appear in future
- with value or error
- with value dependent on some kind of state
- with value and some logging information
- etc
Monad
let us focus on what we want to do with the contained value. Monad
is
like a context
in which the value exists. When we want to do some computation
we are abstracting over the context so we aren’t disrupted whether or
not the value exists, we have many of them or the value may appear in a future.
We want just to get the value out of the container for a moment to make
some computation and then put it there again. The context
is important only when
we want to pull out a value permanently.
Another advantage of the monad
is an ability of sequencing the computations.
Having let’s say two computations we can very easily make dependence
between them saying that the computations of the second depends on
a result of the first. Of course this can be scaled to more than two.
At first glance, it may seem to be not so impressive because it is very common to make such things during coding. But be aware that monad frees us from thinking about the context in which the value exists. The context can be for example an asynchronous computation. Dealing with concurrency is challenging - we have to be very careful to not make a hard to spot mistakes. Monad takes care about this complexity, providing a result of the first computation as soon as possible giving us possibility to spawn another computation in asynchronous manner.
Laws
Each monad needs to follow three laws
- Left identity:
return a >>= f ≡ f a
- Right identity:
m >>= return ≡ m
- Associativity:
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
These laws was taken from haskell because expressions there are very compact and
easy to follow. Function >>=
in scala maps to flatMap
, return
is
just a pure
, f x
is an application of function f
with x
and the
last one \x -> ...
is a lambda expression.
Laws in scala can be written in a following way (using ScalaCheck)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
If you are curious about implementation details take a look on this class
Flavors of monads
This section is a placeholder for a list of posts about monads mentioned in this article. I will try my best to deliver a missing content. Watch my blog for an update.
Monads:
- Option
- Either
- Id
- Writer
- Reader
- State
- Try
- IO
- List