Gilgamesz
Gilgamesz

Reputation: 5073

liftIO instead of composition lift

Down in this document, we call liftIO in eval6 to perform I/O actions. Why do we need to lift in this case? Because there is no IO class for which we can instantiate a type as. Therefore, for I/O actions, we have to call lift to send the commands inwards. For eval6 , we would need to compose lift four times to print something. This is just inconvenient, so people create a new class MonadIO such that we only need to call liftIO once, without having to keep count of how many times to compose lift:

class (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = id
instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where
liftIO = lift . liftIO
instance (MonadIO m) => MonadIO (ReaderT r m) where
liftIO = lift . liftIO

The citation comes from http://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf

And again, I cannot understand why withoud liftIO we would have to call liftIO four times. I cannot see it therefore please make clear it ;)

Upvotes: 1

Views: 165

Answers (1)

dfeuer
dfeuer

Reputation: 48611

class MonadTrans t where
  lift :: Monad m => m a -> t m a

If you want

foo :: Monad m => m a -> ExceptT e (StateT s (ReaderT r m)) a

then you need to apply lift once for each of the type constructors. First we get

lift :: Monad m
     => m a -> ReaderT r m a

then

lift . lift :: Monad m
   => m a -> StateT s (ReaderT r m) a

then

foo = lift . lift . lift :: Monad m
   => m a -> ExceptT e (StateT s (ReaderT r m)) a

Upvotes: 1

Related Questions