A Practical Introduction to Monad Transformers
01 February 2015
Monad transformers combine the functionality of two monads into one. They are often used as a way to “flatten” nested monads, but are also used to enable interactions between monads that, when used seperately, would be incredibly difficult to implement.
You input a number and want to manipulate it while printing the result each time. If there were no intermediate IO operations we could use the state monad with the following state changes:
chain them together:
then get the result:
Now let’s consider how to print the state. If we want to preserve the above chaining syntax, we need a monad where:
- We can do stateful computations
- Our IO functions have access variables inside the computation
This monad is called the state monad transformer.
The state monad transformer is defined as:
1Meaning that given an initial state
s and a state transformer
st, we can call
runStateT st s to get
a monad containing the state tuple.
The real beauty (or magic, as some would say) of this monad comes from the bind function. Let’s take a look at its definition:
Time to break it down.
m is a state transformer.
k is a function that takes a result of type
a, and returns
a state transformer. The final state transformer, when run with an initial state, does the following:
- Gets the result and state of running the computation in
mwith the initial state
- Passes the result of (1) to the function
k, returning a different state transformer
- Runs the computation created in (2) using the state returned in (1)
- Wraps the result
This means that we will be able to keep using a simple chained sequence of monads.
How does this relate to the problem at hand?
The monad component of the state transformer allows us to execute IO operations which have access to the state
during the computation. Here is how the
subtract functions can be written using the state transformer
We can still chain them using the same syntax as before:
and run it:
newtypecreates a strict, isomorphic type with a single value constructor. If all that was too much, just imagine that
newtyperearranges an existing type into a more pleasant one. ↩