Monad transformers allow us to stack monads. Say we have a monad, like
Option, and we want to wrap it in another monad, like
\/, in a convenient way (where convenient is to be defined shortly). Monad transformers let us do this. Scalaz comes with lots of monad transformers. Let’s see how to use them and the benefits they supply.
Let’s motivate monad transformers with the above example. We have an operation that may or may not generate a value. Something like getting a value from a database. Thus we model it as an
Option. This operation may also encounter various other errors. To keep things simple we’ll encode the errors as
Strings. So we basically have three different types of result:
- value found;
- value not found; or
- error occurred.
We can model this as
type Result[+A] = String \/ Option[A]. (Note that
\/ is Scalaz’s version of
Either.) We can construct values of this type by hand:
result we must unwrap it twice, which is tedious:
This is fairly horrible. The desugared version is actually a bit clearer:
Still, can’t we avoid this nesting? It seems like we should be able to chain calls to
flatMap and friends and just have it do the right thing. With monad transformers we can. Here’s the transformed version.
There are three changes:
- the type definitions;
- the way we construct values of these new types; and
- the removal of one layer of nesting when we use the monad.
Let’s go through these in order.
OptionT[M[_], A] is a monad transformer that constructs an
Option[A] inside the monad
M. So the first important point is the monad transformers are built from the inside out.
Note that I define a type alias
Error for the monad we wrap around the
Option. Why is this the case? It has to do with type inference.
M to have a type (technically, a kind) like
M[A]. This is,
M should have a single type parameter.
\/ has two type parameters, the left and the right types. We have to tell Scala has to get from two type parameters to one. One option is to use a type lambda:
Clearly this horror should not be inflicted on the world. A saner option is to just define the type as I have done above with
Constructing values of
Result can be done in a variety of ways, depending on what we want to achieve.
If we want to construct a value in the default way, we can use the
point method like so:
Note that this wraps our value in
Some and a right
What if we want, say, a
None for the option. We can’t use
point as we have above:
The solution is to use the
Here I’ve used Scalaz’s
Error to construct the value of the correct type, before wrapping it in an
If we want to create a left
\/ we go about it the same way:
Note the type declaration, needed to assist type inference.
Using the Monad
When we use our new monad
flatMap do multiple levels of unwrapping for us.
Of course we might not want to unwrap all the layers of our monad. We can manually unwrap our data if need be. All monad transformers in Scalaz return their data if you call the
run method. With this we can do whatever we want, such as folding over the
What about some utility functions to help with this? There are no such methods defined for all monad transformers, that I know of, but in the particular case of
OptionT we can use the
flatMapF method. For a type
OptionT[F[_], A] the normal
flatMap has type
flatMapF has type
(Note I removed an implicit parameter from the method signatures above.)
Result[Int] type this means the parameter
flatMapF should have type
Int => \/[String, B]. Note that
B is not wrapped in an
flatMapF will do this for us. We also don’t have to wrap our result in
Here is an example:
Once you start using monads it’s quite easy to find yourself using a lot of them. For example, if you do asynchronous programming in Scala you are likely using
Future which is a monad1. Debugging futures can be hard. Futures don’t give good stack traces due to the continual context switches, and logs are hard to interpret as output from many threads is mixed together. An alternative is to keep an in-memory log with each computation running in a
Future, and output this log when the
Future finishes. This can be accomplished using the
Writer monad. As soon as you do this you have a stack of monads, and monad transformers become useful. Once you know what to look for you’ll find them everywhere!