Haskell: подъем против лифта

В каких ситуациях следует использовать liftIO? Когда я использую ErrorT String IO, функция lift работает, чтобы поднять IO-действия в ErrorT, поэтому liftIO кажется излишним.

Ответ 1

lift всегда поднимается с "предыдущего" слоя. Если вам нужно подняться со второго слоя, вам понадобится lift . lift и т.д.

С другой стороны, liftIO всегда поднимается с уровня ввода-вывода (который, если присутствует, всегда находится в нижней части стека). Итак, если у вас более двух слоев монад, вы оцените liftIO.

Сравните тип аргумента в следующих 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

Ответ 2

liftIO - это всего лишь ярлык для IO Monad, в зависимости от того, в какой Monad вы находитесь. В принципе, liftIO равен использованию переменного количества лифтов. Сначала это может показаться излишним, но использование liftIO имеет одно большое преимущество: он делает ваш код ввода-вывода независимым от реальной конструкции Monad, поэтому вы можете повторно использовать один и тот же код независимо от того, какое количество слоев было создано из вашей последней Monad (это очень важно при записи монадного трансформатора).

В руке ohter liftIO не приходит бесплатно, так как лифт делает: трансформаторы Monad, которые вы используете, должны иметь поддержку для этого, например. Monad, в котором вы находитесь, должен быть экземпляром класса MonadIO, но в настоящее время большинство Monads (и, конечно же, проверка типов проверит это для вас во время компиляции: сила Haskell!).