On 12/17/07, Jack Kelly <endgame.dos@gmail.com> wrote:
> liftIO $ hPutStrLn h "You lose"
> liftIO $ hFlush h

IO is once again special. For other inner monads, the lift function
does the same thing. Note also that IO has no transformer and must
therefore always be the innermost monad.
 
Actually, this isn't true.  In the case of ReaderT Config IO (), liftIO is the same as lift.
 
liftIO exists because it is more general; it works for any monad that is an instance of "MonadIO", which includes IO, ReaderT r IO, StateT s IO, ReaderT r (StateT s (ErrorT e IO))), etc.
 
In the last case, you could write
    lift $ lift $ lift $ hPutStrLn h "You lose"
but it's much simpler to write
    liftIO $ hPutStrLn h "You lose"
 
Similarily, "ask" is defined in the MonadReader typeclass, which includes Reader, ReaderT IO, and all sorts of other monads that include "Reader" at some level; only the real "reader" monad has a full implementation; the rest are defined in terms of "lift" or simpler operations, like so:
 
instance MonadReader r m => MonadReader r (StateT s m) where
    ask = lift ask
    local f m = StateT $ \s -> local f (runStateT m s)
 
  -- ryan