views:

163

answers:

2

In what situations should liftIO be used? When I'm using ErrorT String IO, the lift function works to lift IO actions into ErrorT, so liftIO seems superfluous.

+11  A: 

lift always lifts from the "previous" layer. If you need to lift from the second layer, you would need lift . lift and so on.

On the other hand, liftIO always lifts from the IO layer (which, when present, is always on the bottom of the stack). So, if you have more than 2 layers of monads, you will appreciate liftIO.

Compare the type of the argument in the following lambdas:

type T = ReaderT Int (WriterT String IO) Bool

> :t \x -> (lift x :: T)
\x -> (lift x :: T) :: WriterT String IO Bool -> T

> :t \x -> (liftIO x :: T)
\x -> (liftIO x :: T) :: IO Bool -> T
Roman Cheplyaka
I generally will use `liftIO` to lift to the IO layer even if `lift` is sufficient, because then I can change the monad stack and the code still works.
John
@John: good point. And also it makes it obvious that you're lifting IO and not any other monad.
Roman Cheplyaka
+4  A: 

liftIO is just a shortcut to the IO Monad, whichever the Monad you are in. Basically, liftIO equals to using a variable number of lifts. At first this might sound redundant but using liftIO has one big advantage: it makes your IO code indpendent of the actual Monad construction so you can reuse the same code no matter the number of layer your final Monad has been built of (this is quite important when writing a monad transformer).

On the ohter hand, liftIO is not coming for free, as lift does: the Monad transformers you're using must have support for it, e.g. the Monad you're in must be an instance of the MonadIO class, but most Monads nowadays do (and of course, the type-checker will check this for you at compile time: that's the strength of Haskell!).

Cristiano Paris