# Monad Transformers Tutorial

### From HaskellWiki

(Difference between revisions)

m |
RickyElrod (Talk | contribs) m (Add missing hask tags around a type.) |
||

(One intermediate revision by one user not shown) | |||

Line 66: | Line 66: | ||

</haskell> |
</haskell> |
||

− | This wouldn't work, because the type of each statement in our do block is <hask>MaybeIO a</hask> and not IO a, so a (print "Hello") which is <hask>IO ()</hask> cannot be put in there. This is where we want to "transform" an <hask>IO a</hask> value to a <hask>MaybeIO a</hask> value. All we have to do is convert the <hask>IO a</hask> to an <hask>IO (Maybe a)</hask> that doesn't "fail" our Maybe monad. So it just means putting a <hask>Just</hask> in there: |
+ | This wouldn't work, because the type of each statement in our do block is <hask>MaybeIO a</hask> and not <hask>IO a</hask>, so a (print "Hello") which is <hask>IO ()</hask> cannot be put in there. This is where we want to "transform" an <hask>IO a</hask> value to a <hask>MaybeIO a</hask> value. All we have to do is convert the <hask>IO a</hask> to an <hask>IO (Maybe a)</hask> that doesn't "fail" our Maybe monad. So it just means putting a <hask>Just</hask> in there: |

<haskell> |
<haskell> |
||

Line 134: | Line 134: | ||

</haskell> |
</haskell> |
||

− | And now "t" is our <hask>MaybeT</hask> (of kind <hask>*->*->*</hask>, i.e: two type parameters) and <hask>lift</hask> is our transformToMaybeT, so: |
+ | And now "t" is our <hask>MaybeT</hask> (of kind <hask>(* -> *) -> * -> *</hask>, i.e: two type parameters) and <hask>lift</hask> is our transformToMaybeT, so: |

<haskell> |
<haskell> |
||

Line 140: | Line 140: | ||

lift = transformToMaybeT |
lift = transformToMaybeT |
||

</haskell> |
</haskell> |
||

+ | |||

+ | Why are they named <hask>Monad Transformers</hask>? Note the kind signature of instances of the MonadTrans class: <hask>(* -> *) -> (* -> *)</hask>. That is, every monad transformer type constructor takes a monad (kind <hask>* -> *</hask>) as an argument, and returns a monad (also <hask>* -> *</hask>) as a result. So all Monad Transformers are basically type-functions from monad types to different monad types which have additional traits. |

## Latest revision as of 19:04, 8 February 2014

Think about code in IO that needs to be able to break out of a loop:

forM_ [1..maxRetries] $ \i -> do response <- request i when (satisfied response) break

Reminder about "when":

when False _ = return () when True a = a

So, how would you implement "break"?

Another example:

do mc1 <- tryConnect "host1" case mc1 of Nothing -> return Nothing Just c1 -> do mc2 <- tryConnect "host2" case mc2 of Nothing -> return Nothing Just c2 -> do ..

(>>=)

(>>=)

IO (Maybe a)

Maybe

IO (Maybe a)

Maybe a

newtype MaybeIO a = MaybeIO { runMaybeIO :: IO (Maybe a) } instance Monad MaybeIO where return x = MaybeIO (return (Just x)) MaybeIO action >>= f = MaybeIO $ do result <- action case result of Nothing -> return Nothing Just x -> runMaybeIO (f x)

So now we can replace the above boilerplate code with:

result <- runMaybeIO $ do c1 <- MaybeIO $ tryConnect "host1" c2 <- MaybeIO $ tryConnect "host2" ..

Or if the tryConnect function wrapped its result in MaybeIO then we just have to use runMaybeIO there, and that's it. What happens if we now have some "print" in between?

result <- runMaybeIO $ do c1 <- MaybeIO $ tryConnect "host1" print "Hello" c2 <- MaybeIO $ tryConnect "host2" ..

MaybeIO a

IO a

IO ()

IO a

MaybeIO a

IO a

IO (Maybe a)

Just

transformIOtoMaybeIO :: IO a -> MaybeIO a transformIOtoMaybeIO action = MaybeIO $ do result <- action return (Just result)

And now we can do:

result <- runMaybeIO $ do c1 <- MaybeIO $ tryConnect "host1" transformIOtoMaybeIO $ print "Hello" c2 <- MaybeIO $ tryConnect "host2" ..

Now we can also break from the first example's loop!

break :: MaybeIO a break = MaybeIO $ return Nothing forM_ [1..maxRetries] $ \ i -> do response <- transformIOtoMaybeIO $ request i when (satisfied response) break

IO (Maybe a)

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) } instance Monad m => Monad (MaybeT m) where return x = MaybeT (return (Just x)) MaybeT action >>= f = MaybeT $ do result <- action case result of Nothing -> return Nothing Just x -> runMaybeT (f x)

That was easy! I just replaced MaybeIO with MaybeT, IO with m, and added an "m" type parameter (with Monad constraint).

transformToMaybeT :: Monad m => m a -> MaybeT m a transformToMaybeT action = MaybeT $ do result <- action return (Just result)

MaybeT

EitherT

lift

transformToMaybeT :: Monad m => m a -> MaybeT m a transformToEitherT :: Monad m => m a -> EitherT l m a

It seems we can capture this pattern with a class:

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

MaybeT

(* -> *) -> * -> *

lift

instance MonadTrans MaybeT where lift = transformToMaybeT

Monad Transformers

(* -> *) -> (* -> *)

* -> *

* -> *