Copyright | (c) Andy Gill 2001 (c) Oregon Graduate Institute of Science and Technology 2001 |
---|---|

License | BSD-style (see the file LICENSE) |

Maintainer | [email protected] |

Stability | experimental |

Portability | portable |

Safe Haskell | Safe |

Language | Haskell98 |

The class of monad transformers.

A monad transformer makes a new monad out of an existing monad, such that computations of the old monad may be embedded in the new one. To construct a monad with a desired set of features, one typically starts with a base monad, such as `Identity`

, `[]`

or `IO`

, and applies a sequence of monad transformers.

class MonadTrans t where Source

The class of monad transformers. Instances should satisfy the following laws, which state that `lift`

is a monad transformation:

lift :: Monad m => m a -> t m a Source

Lift a computation from the argument monad to the constructed monad.

Most monad transformer modules include the special case of applying the transformer to `Identity`

. For example, `State s`

is an abbreviation for `StateT s Identity`

.

Each monad transformer also comes with an operation `run`

*XXX*`T`

to unwrap the transformer, exposing a computation of the inner monad. (Currently these functions are defined as field labels, but in the next major release they will be separate functions.)

All of the monad transformers except `ContT`

and `SelectT`

are functors on the category of monads: in addition to defining a mapping of monads, they also define a mapping from transformations between base monads to transformations between transformed monads, called `map`

*XXX*`T`

. Thus given a monad transformation `t :: M a -> N a`

, the combinator `mapStateT`

constructs a monad transformation

mapStateT t :: StateT s M a -> StateT s N a

For these monad transformers, `lift`

is a natural transformation in the category of monads, i.e. for any monad transformation `t :: M a -> N a`

,

Each of the monad transformers introduces relevant operations. In a sequence of monad transformers, most of these operations.can be lifted through other transformers using `lift`

or the `map`

*XXX*`T`

combinator, but a few with more complex type signatures require specialized lifting combinators, called `lift`

*Op* (see Control.Monad.Signatures).

A monad is said to be *strict* if its `>>=`

operation is strict in its first argument. The base monads `Maybe`

, `[]`

and `IO`

are strict:

>>>undefined >> return 2 :: Maybe Integer*** Exception: Prelude.undefined

However the monad `Identity`

is not:

>>>runIdentity (undefined >> return 2)2

In a strict monad you know when each action is executed, but the monad is not necessarily strict in the return value, or in other components of the monad, such as a state. However you can use `seq`

to create an action that is strict in the component you want evaluated.

The first example is a parser monad in the style of

- "Monadic parsing in Haskell", by Graham Hutton and Erik Meijer,
*Journal of Functional Programming*8(4):437-444, July 1998 (http://www.cs.nott.ac.uk/~pszgmh/bib.html#pearl).

We can define such a parser monad by adding a state (the `String`

remaining to be parsed) to the `[]`

monad, which provides non-determinism:

import Control.Monad.Trans.State type Parser = StateT String []

Then `Parser`

is an instance of `MonadPlus`

: monadic sequencing implements concatenation of parsers, while `mplus`

provides choice. To use parsers, we need a primitive to run a constructed parser on an input string:

runParser :: Parser a -> String -> [a] runParser p s = [x | (x, "") <- runStateT p s]

Finally, we need a primitive parser that matches a single character, from which arbitrarily complex parsers may be constructed:

item :: Parser Char item = do c:cs <- get put cs return c

In this example we use the operations `get`

and `put`

from Control.Monad.Trans.State, which are defined only for monads that are applications of `StateT`

. Alternatively one could use monad classes from the `mtl`

package or similar, which contain methods `get`

and `put`

with types generalized over all suitable monads.

We can define a parser that also counts by adding a `WriterT`

transformer:

import Control.Monad.Trans.Class import Control.Monad.Trans.State import Control.Monad.Trans.Writer import Data.Monoid type Parser = WriterT (Sum Int) (StateT String [])

The function that applies a parser must now unwrap each of the monad transformers in turn:

runParser :: Parser a -> String -> [(a, Int)] runParser p s = [(x, n) | ((x, Sum n), "") <- runStateT (runWriterT p) s]

To define the `item`

parser, we need to lift the `StateT`

operations through the `WriterT`

transformer.

item :: Parser Char item = do c:cs <- lift get lift (put cs) return c

In this case, we were able to do this with `lift`

, but operations with more complex types require special lifting functions, which are provided by monad transformers for which they can be implemented. If you use the monad classes of the `mtl`

package or similar, this lifting is handled automatically by the instances of the classes, and you need only use the generalized methods `get`

and `put`

.

We can also define a primitive using the Writer:

tick :: Parser () tick = tell (Sum 1)

Then the parser will keep track of how many `tick`

s it executes.

This example is a cut-down version of the one in

- "Monad Transformers and Modular Interpreters", by Sheng Liang, Paul Hudak and Mark Jones in
*POPL'95*(http://web.cecs.pdx.edu/~mpj/pubs/modinterp.html).

Suppose we want to define an interpreter that can do I/O and has exceptions, an environment and a modifiable store. We can define a monad that supports all these things as a stack of monad transformers:

import Control.Monad.Trans.Class import Control.Monad.Trans.State import qualified Control.Monad.Trans.Reader as R import qualified Control.Monad.Trans.Except as E import Control.Monad.IO.Class type InterpM = StateT Store (R.ReaderT Env (E.ExceptT Err IO))

for suitable types `Store`

, `Env`

and `Err`

.

Now we would like to be able to use the operations associated with each of those monad transformers on `InterpM`

actions. Since the uppermost monad transformer of `InterpM`

is `StateT`

, it already has the state operations `get`

and `set`

.

The first of the `ReaderT`

operations, `ask`

, is a simple action, so we can lift it through `StateT`

to `InterpM`

using `lift`

:

ask :: InterpM Env ask = lift R.ask

The other `ReaderT`

operation, `local`

, has a suitable type for lifting using `mapStateT`

:

local :: (Env -> Env) -> InterpM a -> InterpM a local f = mapStateT (R.local f)

We also wish to lift the operations of `ExceptT`

through both `ReaderT`

and `StateT`

. For the operation `throwE`

, we know `throwE e`

is a simple action, so we can lift it through the two monad transformers to `InterpM`

with two `lift`

s:

throwE :: Err -> InterpM a throwE e = lift (lift (E.throwE e))

The `catchE`

operation has a more complex type, so we need to use the special-purpose lifting function `liftCatch`

provided by most monad transformers. Here we use the `ReaderT`

version followed by the `StateT`

version:

catchE :: InterpM a -> (Err -> InterpM a) -> InterpM a catchE = liftCatch (R.liftCatch E.catchE)

We could lift `IO`

actions to `InterpM`

using three `lift`

s, but `InterpM`

is automatically an instance of `MonadIO`

, so we can use `liftIO`

instead:

putStr :: String -> InterpM () putStr s = liftIO (Prelude.putStr s)

© The University of Glasgow and others

Licensed under a BSD-style license (see top of the page).

https://downloads.haskell.org/~ghc/8.8.3/docs/html/libraries/transformers-0.5.6.2/Control-Monad-Trans-Class.html