Control.Monad.Morph

mmorph-1.2.0: Monad morphisms

Safe Haskell | Safe
Language | Haskell2010

Control.Monad.Morph

Contents

- Functors over Monads
- Monads over Monads
- Tutorial
  - Generalizing base monads
  - Monad morphisms
  - Mixing diverse transformers
  - Embedding transformers

Description

A monad morphism is a natural transformation:

    morph :: forall a . m a -> n a

... that obeys the following two laws:

    morph $ do x <- m  =  do x <- morph m
               f x           morph (f x)

    morph (return x) = return x

... which are equivalent to the following two functor laws:

    morph . (f >=> g) = morph . f >=> morph . g

    morph . return = return

Examples of monad morphisms include:

- `lift` (from `MonadTrans`)
- `squash` (See below)
- ``hoist` f` (See below), if `f` is a monad morphism
- `(f . g)`, if `f` and `g` are both monad morphisms
- `id`

Monad morphisms commonly arise when manipulating existing monad transformer code for compatibility purposes. The `MFunctor`, `MonadTrans`, and `MMonad` classes define standard ways to change monad transformer stacks:

- `lift` introduces a new monad transformer layer of any type.
- `squash` flattens two identical monad transformer layers into a single layer of the same type.
- `hoist` maps monad morphisms to modify deeper layers of the monad transformer stack.

Synopsis

- class MFunctor (t :: (Type -> Type) -> k -> Type) where
  - hoist :: forall m n (b :: k). Monad m => (forall a. m a -> n a) -> t m b -> t n b
- generalize :: Monad m => Identity a -> m a
- class (MFunctor t, MonadTrans t) => MMonad (t :: (Type -> Type) -> Type -> Type) where
  - embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> t n a) -> t m b -> t n b
- 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
- squash :: forall (m :: Type -> Type) t a. (Monad m, MMonad t) => t (t m) a -> t m a
- (>|>) :: forall (m3 :: Type -> Type) t m1 m2 c. (Monad m3, MMonad t) => (forall a. m1 a -> t m2 a) -> (forall b. m2 b -> t m3 b) -> m1 c -> t m3 c
- (<|<) :: forall (m3 :: Type -> Type) t m2 m1 c. (Monad m3, MMonad t) => (forall b. m2 b -> t m3 b) -> (forall a. m1 a -> t m2 a) -> m1 c -> t m3 c
- (=<|) :: forall (n :: Type -> Type) t m b. (Monad n, MMonad t) => (forall a. m a -> t n a) -> t m b -> t n b
- (|>=) :: forall (n :: Type -> Type) t m b. (Monad n, MMonad t) => t m b -> (forall a. m a -> t n a) -> t n b

# Functors over Monads

class MFunctor (t :: (Type -> Type) -> k -> Type) where #

A functor in the category of monads, using `hoist` as the analog of `fmap`:

    hoist (f . g) = hoist f . hoist g

    hoist id = id

Methods

hoist :: forall m n (b :: k). Monad m => (forall a. m a -> n a) -> t m b -> t n b #

Lift a monad morphism from `m` to `n` into a monad morphism from `(t m)` to `(t n)`

The first argument to `hoist` must be a monad morphism, even though the type system does not enforce this

#### Instances

Instances details

MFunctor Lift # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> Lift m b -> Lift n b #
MFunctor MaybeT # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> MaybeT m b -> MaybeT n b #
MFunctor (Backwards :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> Backwards m b -> Backwards n b #
MFunctor (ExceptT e :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> ExceptT e m b -> ExceptT e n b #
MFunctor (IdentityT :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> IdentityT m b -> IdentityT n b #
MFunctor (ReaderT r :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> ReaderT r m b -> ReaderT r n b #
MFunctor (StateT s :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> StateT s m b -> StateT s n b #
MFunctor (StateT s :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> StateT s m b -> StateT s n b #
MFunctor (WriterT w :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> WriterT w m b -> WriterT w n b #
MFunctor (WriterT w :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> WriterT w m b -> WriterT w n b #
MFunctor (Product f :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> Product f m b -> Product f n b #
(MFunctor f, MFunctor g, forall (m :: Type -> Type). Monad m => Monad (g m)) => MFunctor (ComposeT f g :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Trans.Compose Methods hoist :: Monad m => (forall a. m a -> n a) -> ComposeT f g m b -> ComposeT f g n b #
Functor f => MFunctor (Compose f :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> Compose f m b -> Compose f n b #
MFunctor (RWST r w s :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> RWST r w s m b -> RWST r w s n b #
MFunctor (RWST r w s :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods hoist :: Monad m => (forall a. m a -> n a) -> RWST r w s m b -> RWST r w s n b #

generalize :: Monad m => Identity a -> m a #

A function that `generalize`s the `Identity` base monad to be any monad.

# Monads over Monads

class (MFunctor t, MonadTrans t) => MMonad (t :: (Type -> Type) -> Type -> Type) where #

A monad in the category of monads, using `lift` from `MonadTrans` as the analog of `return` and `embed` as the analog of (`=<<`):

    embed lift = id

    embed f (lift m) = f m

    embed g (embed f t) = embed (\m -> embed g (f m)) t

Methods

embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> t n a) -> t m b -> t n b #

Embed a newly created `MMonad` layer within an existing layer

`embed` is analogous to (`=<<`)

#### Instances

Instances details

MMonad MaybeT # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> MaybeT n a) -> MaybeT m b -> MaybeT n b #
MMonad (ExceptT e) # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> ExceptT e n a) -> ExceptT e m b -> ExceptT e n b #
MMonad (IdentityT :: (Type -> Type) -> Type -> Type) # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> IdentityT n a) -> IdentityT m b -> IdentityT n b #
MMonad (ReaderT r) # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> ReaderT r n a) -> ReaderT r m b -> ReaderT r n b #
Monoid w => MMonad (WriterT w) # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> WriterT w n a) -> WriterT w m b -> WriterT w n b #
Monoid w => MMonad (WriterT w) # |
Instance details Defined in Control.Monad.Morph Methods embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> WriterT w n a) -> WriterT w m b -> WriterT w n b #

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

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 #

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

#### Instances

Instances details

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

squash :: forall (m :: Type -> Type) t a. (Monad m, MMonad t) => t (t m) a -> t m a #

Squash two `MMonad` layers into a single layer

`squash` is analogous to `join`

(>|>) :: forall (m3 :: Type -> Type) t m1 m2 c. (Monad m3, MMonad t) => (forall a. m1 a -> t m2 a) -> (forall b. m2 b -> t m3 b) -> m1 c -> t m3 c infixr 2 #

Compose two `MMonad` layer-building functions

(`>|>`) is analogous to (`>=>`)

(<|<) :: forall (m3 :: Type -> Type) t m2 m1 c. (Monad m3, MMonad t) => (forall b. m2 b -> t m3 b) -> (forall a. m1 a -> t m2 a) -> m1 c -> t m3 c infixl 2 #

Equivalent to (`>|>`) with the arguments flipped

(`<|<`) is analogous to (`<=<`)

(=<|) :: forall (n :: Type -> Type) t m b. (Monad n, MMonad t) => (forall a. m a -> t n a) -> t m b -> t n b infixr 2 #

An infix operator equivalent to `embed`

(`=<|`) is analogous to (`=<<`)

(|>=) :: forall (n :: Type -> Type) t m b. (Monad n, MMonad t) => t m b -> (forall a. m a -> t n a) -> t n b infixl 2 #

Equivalent to (`=<|`) with the arguments flipped

(`|>=`) is analogous to (`>>=`)

# Tutorial

Monad morphisms solve the common problem of fixing monadic code after the fact without modifying the original source code or type signatures. The following sections illustrate various examples of transparently modifying existing functions.

## Generalizing base monads

Imagine that some library provided the following `State` code:

    import Control.Monad.Trans.State

    tick :: State Int ()
    tick = modify (+1)

... but we would prefer to reuse `tick` within a larger `(`StateT` Int `IO`)` block in order to mix in `IO` actions.

We could patch the original library to generalize `tick`'s type signature:

    tick :: (Monad m) => StateT Int m ()

... but we would prefer not to fork upstream code if possible. How could we generalize `tick`'s type without modifying the original code?

We can solve this if we realize that `State` is a type synonym for `StateT` with an `Identity` base monad:

    type State s = StateT s Identity

... which means that `tick`'s true type is actually:

    tick :: StateT Int Identity ()

Now all we need is a function that `generalize`s the `Identity` base monad to be any monad:

    import Data.Functor.Identity

    generalize :: (Monad m) => Identity a -> m a
    generalize m = return (runIdentity m)

... which we can `hoist` to change `tick`'s base monad:

    hoist :: (Monad m, MFunctor t) => (forall a . m a -> n a) -> t m b -> t n b

    hoist generalize :: (Monad m, MFunctor t) => t Identity b -> t m b

    hoist generalize tick :: (Monad m) => StateT Int m ()

This lets us mix `tick` alongside `IO` using `lift`:

    import Control.Monad.Morph
    import Control.Monad.Trans.Class

    tock                        ::                   StateT Int IO ()
    tock = do
        hoist generalize tick   :: (Monad      m) => StateT Int m  ()
        lift $ putStrLn "Tock!" :: (MonadTrans t) => t          IO ()

    >>> runStateT tock 0
    Tock!
    ((), 1)

## Monad morphisms

Notice that `generalize` is a monad morphism, and the following two proofs show how `generalize` satisfies the monad morphism laws. You can refer to these proofs as an example for how to prove a function obeys the monad morphism laws:

    generalize (return x)

    -- Definition of 'return' for the Identity monad
    = generalize (Identity x)

    -- Definition of 'generalize'
    = return (runIdentity (Identity x))

    -- runIdentity (Identity x) = x
    = return x

    generalize $ do x <- m
                    f x

    -- Definition of (>>=) for the Identity monad
    = generalize (f (runIdentity m))

    -- Definition of 'generalize'
    = return (runIdentity (f (runIdentity m)))

    -- Monad law: Left identity
    = do x <- return (runIdentity m)
         return (runIdentity (f x))

    -- Definition of 'generalize' in reverse
    = do x <- generalize m
         generalize (f x)

## Mixing diverse transformers

You can combine `hoist` and `lift` to insert arbitrary layers anywhere within a monad transformer stack. This comes in handy when interleaving two diverse stacks.

For example, we might want to combine the following `save` function:

    import Control.Monad.Trans.Writer

    -- i.e. :: StateT Int (WriterT [Int] Identity) ()
    save    :: StateT Int (Writer  [Int]) ()
    save = do
        n <- get
        lift $ tell [n]

... with our previous `tock` function:

    tock :: StateT Int IO ()

However, `save` and `tock` differ in two ways:

- `tock` lacks a `WriterT` layer
- `save` has an `Identity` base monad

We can mix the two by inserting a `WriterT` layer for `tock` and generalizing `save`'s base monad:

    import Control.Monad

    program ::                   StateT Int (WriterT [Int] IO) ()
    program = replicateM_ 4 $ do
        hoist lift tock
            :: (MonadTrans t) => StateT Int (t             IO) ()
        hoist (hoist generalize) save
            :: (Monad      m) => StateT Int (WriterT [Int] m ) ()

    >>> execWriterT (runStateT program 0)
    Tock!
    Tock!
    Tock!
    Tock!
    [1,2,3,4]

## Embedding transformers

Suppose we decided to `check` all `IOException`s using a combination of `try` and `ErrorT`:

    import Control.Exception
    import Control.Monad.Trans.Class
    import Control.Monad.Trans.Error

    check :: IO a -> ErrorT IOException IO a
    check io = ErrorT (try io)

... but then we forget to use `check` in one spot, mistakenly using `lift` instead:

    program :: ErrorT IOException IO ()
    program = do
        str <- lift $ readFile "test.txt"
        check $ putStr str

    >>> runErrorT program
    *** Exception: test.txt: openFile: does not exist (No such file or directory)

How could we go back and fix `program` without modifying its source code?

Well, `check` is a monad morphism, but we can't `hoist` it to modify the base monad because then we get two `ErrorT` layers instead of one:

    hoist check :: (MFunctor t) => t IO a -> t (ErrorT IOException IO) a

    hoist check program :: ErrorT IOException (ErrorT IOException IO) ()

We'd prefer to `embed` all newly generated exceptions in the existing `ErrorT` layer:

    embed check :: ErrorT IOException IO a -> ErrorT IOException IO a

    embed check program :: ErrorT IOException IO ()

This correctly checks the exceptions that slipped through the cracks:

    >>> import Control.Monad.Morph
    >>> runErrorT (embed check program)
    Left test.txt: openFile: does not exist (No such file or directory)