# Monad Transformers Tutorial

### From HaskellWiki

(Difference between revisions)

m |
(Fix kind signature. Explain the name.) |
||

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

## Revision as of 21:41, 7 February 2011

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

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

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

* -> *

* -> *