1

Sometimes I need to use several nested MonadTrans. For example, I would put a MaybeT inside of a ExceptT, to mimick continue and break in imperative programming:

runExceptT . forM [1..] $ \ _ -> runMaybeT $ do
    ...
    mzero   -- this mimics continue
    lift $ throwE "..."    -- this mimics break
    lift . lift $ putStrLn "Hello!"
    ...

However, as the above code shows, every time I need to do any IO inside of this "artificial loop", I need to put an ugly lift . lift before it. Imagine if I have even more complicated nestings, and a lot of IO operations, this quickly becomes an anonyance. How I make the code cleaner and more concise?

xzhu
  • 5,675
  • 4
  • 32
  • 52
  • 4
    Does using `liftIO` work? – ErikR Oct 15 '15 at 03:22
  • 1
    Note that in some cases it's more efficient to lift things in blocks. `lift $ do {x;y;z}` should be preferred to `do {lift x; lift y; lift z}`. If this turns out to be an issue in a tight loop, you might want `lift $ do {blah; lift bloop}`. – dfeuer Oct 15 '15 at 03:35
  • Possible duplicate of [Flatten monad stack](http://stackoverflow.com/questions/32551152/flatten-monad-stack) – jakubdaniel Oct 15 '15 at 07:06
  • 1
    Note that @dfeuer's refactoring is guaranteed to be safe by the monad transformer laws. – Rein Henrichs Oct 15 '15 at 08:15

1 Answers1

6

For the particular case of IO, you can use liftIO :: MonadIO m => IO a -> m a to lift an IO action through an arbitrarily deep monad transformer stack:

liftIO $ putStrLn "Hello"

This expression has type MonadIO m => m (). Any transformer stack that contains an instance of MonadIO somewhere inside of it should have a MonadIO instance itself, which is why this works for a stack of any depth.

David Young
  • 10,713
  • 2
  • 33
  • 47