# Monad Transformers Tutorial

### From HaskellWiki

Revision as of 19:04, 8 February 2014 by RickyElrod (Talk | contribs)

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

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

* -> *

* -> *