© Stefania Loredana Nita and Marius Mihailescu 2019
Stefania Loredana Nita and Marius MihailescuHaskell Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4507-1_13

13. Monad Transformers

Stefania Loredana Nita1  and Marius Mihailescu1
(1)
Bucharest, Romania
 

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.

In Haskell, there are two main packages related to monad transformers.
  • 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.

The following are some examples of transformers (m in the following examples represents an arbitrary monad ):
  • 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].

The first step is to define a new type.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

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.

You saw in the previous chapter that all monads are instances of the Monad class, so you need to make MaybeT an instance of Monad. Remember that a monad needs to contain the return function and the binding operation (>>=).
instance Monad m => Monad (MaybeT m) where
  return  = MaybeT . return . Just
-- (>>=) :: MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
  x >>= f = MaybeT $ do maybe_value <- runMaybeT x
                        case maybe_value of
                           Nothing    -> return Nothing
                           Just value -> runMaybeT $ f value

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.

The function maybe_value is defined in terms of the bind operator from Maybe.
maybe_value >>= f = case maybe_value of
                        Nothing -> Nothing
                        Just value -> f value

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.

Next, MonadT m needs to be set as an instance of Monad, Applicative, and Functor.
instance Monad m => Applicative (MaybeT m) where
    pure = return
    (<*>) = ap
instance Monad m => Functor (MaybeT m) where
    fmap = liftM
It is natural to make a MaybeT m instance of MonadTrans, because all transformers are instances of MonadTrans. They are all instances of Alternative and MonadPlus as well, because Maybe is an instance of these two.
import Control.Applicative
import Control.Monad (MonadPlus, liftM, ap)
import Control.Monad.Trans
import Control.Monad (mplus, mzero, liftM, ap)
instance Monad m => Alternative (MaybeT m) where
    empty   = MaybeT $ return Nothing
    x <|> y = MaybeT $ do maybe_value <- runMaybeT x
                          case maybe_value of
                               Nothing    -> runMaybeT y
                               Just _     -> return maybe_value
instance Monad m => MonadPlus (MaybeT m) where
    mzero = empty
    mplus = (<|>)
instance MonadTrans MaybeT where
    lift = MaybeT . (liftM Just)

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.

Let’s write the example.
import Control.Monad.Trans.Class (lift)
import Data.Functor.Identity (Identity, runIdentity)
import Control.Monad.Trans.Reader (ReaderT, ask, runReader)
import Control.Monad.Trans.Writer (WriterT, tell, runWriter)
type DataIn = Integer
type DataOut = [String]
type Outcome = Integer
transformersStack :: WriterT DataOut (ReaderT DataIn Identity) Outcome
transformersStack = do
    y <- lift ask
    tell ["The user introduced: " ++ show y]
    return y
To test this, you need to import Control.Monad.Trans.Writer and Control.Monad.Trans.Reader and then load your file (called TransformersStack.hs ).
Prelude> import Control.Monad.Trans.Writer
Prelude Control.Monad.Trans.Writer> import Control.Monad.Trans.Reader
Prelude Control.Monad.Trans.Writer Control.Monad.Trans.Reader> :load TransformersStack.hs
[1 of 1] Compiling Main      ( TransformersStack.hs, interpreted )
Ok, one module loaded.
*Main Control.Monad.Trans.Writer Control.Monad.Trans.Reader> let myReader = runWriterT transformersStack
*Main Control.Monad.Trans.Writer Control.Monad.Trans.Reader> let myIdentity = runReaderT myReader 7
*Main Control.Monad.Trans.Writer Control.Monad.Trans.Reader> runIdentity myIdentity
(7,["The user introduced: 7"])

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. 1.

    M. P. Jones, “Functional Programming with Overloading and Higher-Order Polymorphism,” International School on Advanced Functional Programming(Springer, 1995)

     
  2. 2.

    Transformers: concrete functor and monad transformers, http://hackage.haskell.org/package/transformers

     
  3. 3.

    mtl: monad classes, using functional dependencies, http://hackage.haskell.org/package/mtl

     
  4. 4.
     
  5. 5.
     
  6. 6.
     
  7. 7.
     
  8. 8.
     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.46.96