В каких ситуациях следует liftIO
использовать? Когда я использую ErrorT String IO
, lift
функция работает, чтобы поднять действия ввода-вывода ErrorT
, поэтому liftIO
кажется излишней.
haskell
monad-transformers
Лахлан
источник
источник
liftIO
для подъема на уровень ввода-вывода, даже если этогоlift
достаточно, потому что тогда я могу изменить стек монад, и код все еще работает.lift
в этом ответе (и вопросе) предполагается, что онControl.Monad.Trans.Class
, я думаю? Не тотMonadic lifting
или общий подъем, как описано в первом разделе здесь ?liftIO - это просто ярлык для IO Monad, в какой бы монаде вы ни находились. В основном, liftIO означает использование переменного количества подъемов. Сначала это может показаться избыточным, но использование liftIO имеет одно большое преимущество: он делает ваш код ввода-вывода независимым от фактической конструкции монады, поэтому вы можете повторно использовать один и тот же код независимо от количества слоев, из которых была построена ваша окончательная монада (это очень важно. при написании преобразователя монад).
С другой стороны, liftIO не предоставляется бесплатно, в отличие от lift: трансформаторы Monad, которые вы используете, должны иметь его поддержку, например, монада, в которой вы находитесь, должна быть экземпляром класса MonadIO, но большинство монад в настоящее время поддерживают его. (и, конечно же, программа проверки типов проверит это для вас во время компиляции: в этом сила Haskell!).
источник
Все предыдущие ответы хорошо объясняют разницу. Я просто хотел пролить свет на внутреннюю работу, чтобы было легче понять, почему
liftIO
что-то не волшебное (для начинающих хаскеллеров вроде меня).liftIO :: IO a -> m a
это мудрый инструмент, просто опирающийся на
lift :: (Control.Monad.Trans.Class.MonadTrans t, Monad m) => m a -> t m a
и чаще всего используется, когда используется нижняя монада
IO
. ДляIO
монады это определение довольно простое.class (Monad m) => MonadIO m where liftIO :: IO a -> m a instance MonadIO IO where liftIO = id
Это просто ...
liftIO
на самом деле толькоid
дляIO
монады и, по сути,IO
единственное, что входит в определение класса типа.Дело в том, что когда у нас есть тип монады, который состоит из нескольких слоев преобразователей монад
IO
, нам лучше иметьMonadIO
экземпляр для каждого из этих слоев преобразователей монад. Например,MonadIO
экземплярMaybeT m
требует,m
чтобы он былMonadIO
класс типов , а также.Написание
MonadIO
экземпляра - тоже очень простая задача. ПосколькуMaybeT m
это определяется какinstance (MonadIO m) => MonadIO (MaybeT m) where liftIO = lift . liftIO
или для
StateT s m
instance (MonadIO m) => MonadIO (StateT s m) where liftIO = lift . liftIO
они все одинаковы. Представьте, что у вас есть 4-слойный трансформаторный стек, который вам либо нужно,
lift . lift . lift . lift $ myIOAction
либо просто сделатьliftIO myIOAction
. Если вы думаете об этом, каждыйlift . liftIO
будет считать вас один слой вниз в стеке вверх , пока он не копает весь путь внизIO
в которомliftIO
определяется какid
и дорабатывает с тем же кодом , как в составеlift
с выше.Вот почему, в основном, независимо от конфигурации стека трансформаторов, при условии, что все нижележащие слои являются его членами,
MonadIO
аMonadTrans
одинliftIO
- в порядке.источник