Control.Monad.Trans.Class

transformers-0.6.1.1: Concrete functor and monad transformers

Copyright | (c) Andy Gill 2001 (c) Oregon Graduate Institute of Science and Technology 2001
License | BSD-style (see the file LICENSE)
Maintainer | R.Paterson@city.ac.uk
Stability | experimental
Portability | portable
Safe Haskell | Safe
Language | Haskell2010

Control.Monad.Trans.Class

Contents

- Transformer class
- Conventions
- Strict monads
- Examples
  - Parsing
  - Parsing and counting
  - Interpreter monad

Description

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.

Synopsis

- class (forall (m :: Type -> Type). Monad m => Monad (t m)) => MonadTrans (t :: (Type -> Type) -> Type -> Type) where
  - lift :: Monad m => m a -> t m a

# Transformer class

class (forall (m :: Type -> Type). Monad m => Monad (t m)) => MonadTrans (t :: (Type -> Type) -> Type -> Type) where Source #

The class of monad transformers. For any monad `m`, the result `t m` should also be a monad, and `lift` should be a monad transformation from `m` to `t m`, i.e. it should satisfy the following laws:

-     lift . return = return

-     lift (m >>= f) = lift m >>= (lift . f)

Since 0.6.0.0 and for GHC 8.6 and later, the requirement that `t m` be a `Monad` is enforced by the implication constraint `forall m. `Monad` m => `Monad` (t m)` enabled by the `QuantifiedConstraints` extension.

### Ambiguity error with GHC 9.0 to 9.2.2

Expand

These versions of GHC have a bug (https://gitlab.haskell.org/ghc/ghc/-/issues/20582) which causes constraints like

    (MonadTrans t, forall m. Monad m => Monad (t m)) => ...

to be reported as ambiguous. For transformers 0.6 and later, this can be fixed by removing the second constraint, which is implied by the first.

Methods

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

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

#### Instances

Instances details

MonadTrans MaybeT Source # |
Instance details Defined in Control.Monad.Trans.Maybe Methods lift :: Monad m => m a -> MaybeT m a Source #
Monoid w => MonadTrans (AccumT w) Source # |
Instance details Defined in Control.Monad.Trans.Accum Methods lift :: Monad m => m a -> AccumT w m a Source #
MonadTrans (ExceptT e) Source # |
Instance details Defined in Control.Monad.Trans.Except Methods lift :: Monad m => m a -> ExceptT e m a Source #
MonadTrans (IdentityT :: (Type -> Type) -> Type -> Type) Source # |
Instance details Defined in Control.Monad.Trans.Identity Methods lift :: Monad m => m a -> IdentityT m a Source #
MonadTrans (ReaderT r) Source # |
Instance details Defined in Control.Monad.Trans.Reader Methods lift :: Monad m => m a -> ReaderT r m a Source #
MonadTrans (SelectT r) Source # |
Instance details Defined in Control.Monad.Trans.Select Methods lift :: Monad m => m a -> SelectT r m a Source #
MonadTrans (StateT s) Source # |
Instance details Defined in Control.Monad.Trans.State.Lazy Methods lift :: Monad m => m a -> StateT s m a Source #
MonadTrans (StateT s) Source # |
Instance details Defined in Control.Monad.Trans.State.Strict Methods lift :: Monad m => m a -> StateT s m a Source #
MonadTrans (WriterT w) Source # |
Instance details Defined in Control.Monad.Trans.Writer.CPS Methods lift :: Monad m => m a -> WriterT w m a Source #
Monoid w => MonadTrans (WriterT w) Source # |
Instance details Defined in Control.Monad.Trans.Writer.Lazy Methods lift :: Monad m => m a -> WriterT w m a Source #
Monoid w => MonadTrans (WriterT w) Source # |
Instance details Defined in Control.Monad.Trans.Writer.Strict Methods lift :: Monad m => m a -> WriterT w m a Source #
MonadTrans (ContT r) Source # |
Instance details Defined in Control.Monad.Trans.Cont Methods lift :: Monad m => m a -> ContT r m a Source #
MonadTrans (RWST r w s) Source # |
Instance details Defined in Control.Monad.Trans.RWS.CPS Methods lift :: Monad m => m a -> RWST r w s m a Source #
Monoid w => MonadTrans (RWST r w s) Source # |
Instance details Defined in Control.Monad.Trans.RWS.Lazy Methods lift :: Monad m => m a -> RWST r w s m a Source #
Monoid w => MonadTrans (RWST r w s) Source # |
Instance details Defined in Control.Monad.Trans.RWS.Strict Methods lift :: Monad m => m a -> RWST r w s m a Source #

# Conventions

All monad transformer modules except `Maybe` include the special case of applying the transformer to `Identity`. For example, ``State` s` is an abbreviation for ``StateT` s `Identity``. As a consequence, operations defined on the monad transformer can also be used on this special case.

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 a future major release they may 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`,

- `map`XXX`T t . `lift` = `lift` . t`

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

# Strict monads

A monad is said to be strict if its `>>=` operation (and therefore also `>>`) is strict in its first argument. The base monads `Maybe`, `[]` and `IO` are strict:

    >>> undefined >> Just 2
    *** Exception: Prelude.undefined
    >>> undefined >> [2]
    *** Exception: Prelude.undefined
    >>> undefined >> print 2
    *** Exception: Prelude.undefined

However, the monads `Identity` and `(->) a` are not:

    >>> undefined >> Identity 2
    Identity 2
    >>> (undefined >> (+1)) 5
    6

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.

# Examples

## Parsing

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.

## Parsing and counting

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.

## Interpreter monad

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)