In the previous chapter, you saw how useful monads are. But what if you need operations from two different monads? In this chapter, you will learn how to proceed in such scenarios.
Simple Transformers
We’ll start with a brief description of transformers: a transformer represents a special type that allows you to combine two monads into one monad that uses the properties of both monads . The result of combining two monads is also a monad.
transformers, which includes the monad transformer class and actual transformers. The most commonly used classes in this package are MonadTrans and MonadIO. All transformers in this package are instances of MonadTrans, and they are used to create new monads from existing monads.
mtl (Monad Transform Library ), which includes instances of different monad transformers, based on functional dependencies.
These packages are compatible for use together; they have in common classes, type constructors, and functions, although the names of the modules are different. The transformers package is independent of functional dependencies, being more portable than mtl. Still, the operations in one monad need to be lifted by the programmer in the resulting monad since the actual transformers do not have monad classes.
When you create a monad transformer, the convention is that the resulting monad keeps the name of the base monad, followed by the T character. For example, the transformer for the Maybe monad is MaybeT.
MaybeT: A monad incorporates Maybe a, resulting in m (Maybe a).
ReaderT: The result a in Reader r a is incorporated by another monad, resulting in r-> m a.
StateT: From State s, a monad incorporates the return value and its state (a,s), resulting in s -> m (a,s).
ExceptT: A monad incorporates Either e a, resulting in m (Either e a).
MaybeT Transformer
In this section, you will see how the MaybeT transformer was obtained from the Maybe monad. The code in this section is based on [6].
Here, the type constructor is MaybeT, with the parameter m and a term constructor of MaybeT. You can access this representation through the function runMaybeT.
Let’s take a look at the binding operation, which wraps and unwraps a few times. In the first instruction from the do block into the runMaybeT, the value x is unwrapped into the m (Maybe a) computation, from which Maybe a are extracted through ->. In the case statement , maybe_value is tested, returning Nothing into m in the case of Nothing, while, in the case of Just, f is applied on the value of Just. In addition, in the Just case, runMaybeT puts the output into monad m, because the result of f has type MaybeT m b. The type of the entire do block is m (Maybe b), which is wrapped into the MaybeT constructor.
Although the runMaybeT is in do block, you need to use the MaybeT constructor before do, because the do block needs to be in m, not in MaybeT m.
The function lift from MonadTrans takes functions from the monad m, bringing them into the MaybeT m monad. In this way, they can be used in do blocks.
Building a Simple Monad Transformer Stack
In this section, you will use the following monads in this order to build a monad transformer stack : Identity,1 Reader,2 and Writer.3 The Identity monad does not do anything important, but it is useful in certain situations. The Reader monad can be used when you want to add information to a pure function. Lastly, the Writer monad can be used when you want to add logging to a function.
Another thing you need is the lift 4 function from Control.Monad.Trans.Class, which takes a computation from a monad and allows you to use that computation in another monad. It is useful when you want to work with the ask function from Reader, which is contained in a transformer stack.
The Identity monad is a base monad that lets you put things on the top (the other base monad is IO). Before the Identity monad is the Reader monad, which holds a number in the previous example. At the top of the transformers stack is the Write monad, which takes a list of strings.
The external Writer monad could be used directly with tell, but the calls needs to be wrapped into the internal Reader . This is done by the lift function. Note that the lift function is useful in the monad transformer stack no matter the number of monads.
Summary
In this chapter, you learned what monad transformers are and how they can be used. You saw a simple example of building a monad transformer. Finally, you learned how to create a stack of monad transformers.
References
- 1.
M. P. Jones, “Functional Programming with Overloading and Higher-Order Polymorphism,” International School on Advanced Functional Programming(Springer, 1995)
- 2.
Transformers: concrete functor and monad transformers, http://hackage.haskell.org/package/transformers
- 3.
mtl: monad classes, using functional dependencies, http://hackage.haskell.org/package/mtl
- 4.
Monad transformers, https://wiki.haskell.org/Monad_Transformers
- 5.
Monad transformers, https://www.schoolofhaskell.com/user/commercial/content/monad-transformers
- 6.
All about monads, https://wiki.haskell.org/All_About_Monads
- 7.
Haskell/monad transformers, https://en.wikibooks.org/wiki/Haskell/Monad_transformers
- 8.
All about monads, https://wiki.haskell.org/All_About_Monads#Monad_transformers