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 # Functors over Monads class MFunctor t where Source # A functor in the category of monads, using hoist as the analog of fmap: hoist (f . g) = hoist f . hoist g hoist id = id Minimal complete definition hoist Methods hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # 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  Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # Functor f => MFunctor * (Compose * * f) Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # MFunctor * (RWST r w s) Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # MFunctor * (RWST r w s) Source # Methodshoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b Source # generalize :: Monad m => Identity a -> m a Source # A function that generalizes the Identity base monad to be any monad. # Monads over Monads class (MFunctor t, MonadTrans t) => MMonad t where Source # 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 Minimal complete definition embed Methods embed :: Monad n => (forall a. m a -> t n a) -> t m b -> t n b Source # Embed a newly created MMonad layer within an existing layer embed is analogous to (=<<) Instances  Source # Methodsembed :: Monad n => (forall a. m a -> ListT n a) -> ListT m b -> ListT n b Source # Source # Methodsembed :: Monad n => (forall a. m a -> MaybeT n a) -> MaybeT m b -> MaybeT n b Source # Source # Methodsembed :: Monad n => (forall a. m a -> IdentityT * n a) -> IdentityT * m b -> IdentityT * n b Source # Error e => MMonad (ErrorT e) Source # Methodsembed :: Monad n => (forall a. m a -> ErrorT e n a) -> ErrorT e m b -> ErrorT e n b Source # Source # Methodsembed :: Monad n => (forall a. m a -> ExceptT e n a) -> ExceptT e m b -> ExceptT e n b Source # Monoid w => MMonad (WriterT w) Source # Methodsembed :: Monad n => (forall a. m a -> WriterT w n a) -> WriterT w m b -> WriterT w n b Source # Monoid w => MMonad (WriterT w) Source # Methodsembed :: Monad n => (forall a. m a -> WriterT w n a) -> WriterT w m b -> WriterT w n b Source # Source # Methodsembed :: Monad n => (forall a. m a -> ReaderT * r n a) -> ReaderT * r m b -> ReaderT * r n b Source # class MonadTrans (t :: (* -> *) -> * -> *) where # The class of monad transformers. Instances should satisfy the following laws, which state that lift is a monad transformation: • lift . return = return • lift (m >>= f) = lift m >>= (lift . f) Minimal complete definition lift Methods lift :: Monad m => m a -> t m a # Lift a computation from the argument monad to the constructed monad. Instances  Methodslift :: Monad m => m a -> ListT m a # Methodslift :: Monad m => m a -> MaybeT m a # Methodslift :: Monad m => m a -> IdentityT * m a # Methodslift :: Monad m => m a -> ErrorT e m a # Methodslift :: Monad m => m a -> ExceptT e m a # Methodslift :: Monad m => m a -> StateT s m a # Methodslift :: Monad m => m a -> StateT s m a # Monoid w => MonadTrans (WriterT w) Methodslift :: Monad m => m a -> WriterT w m a # Monoid w => MonadTrans (WriterT w) Methodslift :: Monad m => m a -> WriterT w m a # Methodslift :: Monad m => m a -> ContT * r m a # Methodslift :: Monad m => m a -> ReaderT * r m a # (MFunctor * f, MonadTrans f, MonadTrans g) => MonadTrans (ComposeT f g) # Methodslift :: Monad m => m a -> ComposeT f g m a # Monoid w => MonadTrans (RWST r w s) Methodslift :: Monad m => m a -> RWST r w s m a # Monoid w => MonadTrans (RWST r w s) Methodslift :: Monad m => m a -> RWST r w s m a # squash :: (Monad m, MMonad t) => t (t m) a -> t m a Source # Squash two MMonad layers into a single layer squash is analogous to join (>|>) :: (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 Source # Compose two MMonad layer-building functions (>|>) is analogous to (>=>) (<|<) :: (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 Source # Equivalent to (>|>) with the arguments flipped (<|<) is analogous to (<=<) (=<|) :: (Monad n, MMonad t) => (forall a. m a -> t n a) -> t m b -> t n b infixr 2 Source # An infix operator equivalent to embed (=<|) is analogous to (=<<) (|>=) :: (Monad n, MMonad t) => t m b -> (forall a. m a -> t n a) -> t n b infixl 2 Source # 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 generalizes 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)


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