[英]Haskell: lift vs liftIO
In what situations should liftIO
be used?什么情况下应该使用liftIO
? When I'm using ErrorT String IO
, the lift
function works to lift IO actions into ErrorT
, so liftIO
seems superfluous.当我使用ErrorT String IO
, lift
函数可以将 IO 操作提升到ErrorT
,因此liftIO
似乎是多余的。
lift
always lifts from the "previous" layer. lift
总是从“上一个”层lift
。 If you need to lift from the second layer, you would need lift . lift
如果您需要从第二层提升,则需要lift . lift
lift . lift
and so on. lift . lift
等。
On the other hand, liftIO
always lifts from the IO layer (which, when present, is always on the bottom of the stack).另一方面, liftIO
总是从 IO 层提升(当存在时,它总是在堆栈的底部)。 So, if you have more than 2 layers of monads, you will appreciate liftIO
.所以,如果你有超过 2 层的 monad,你会喜欢liftIO
。
Compare the type of the argument in the following lambdas:比较以下 lambda 表达式中参数的类型:
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
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. LiftIO 只是 IO Monad 的快捷方式,无论您在哪个 Monad 中。基本上,liftIO 等于使用可变数量的电梯。 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).起初这可能听起来有些多余,但使用liftIO有一个很大的优势:它使您的IO代码独立于实际的Monad构造,因此无论您的最终Monad构建了多少层,您都可以重用相同的代码(这非常重要在编写 monad 转换器时)。
On the ohter hand, liftIO is not coming for free, as lift does: the Monad transformers you're using must have support for it, eg 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!).另一方面,liftIO 不会像lift 那样免费提供:您使用的Monad 转换器必须支持它,例如您所在的Monad 必须是MonadIO 类的一个实例,但现在大多数Monad 都支持(当然,类型检查器会在编译时为您检查:这就是 Haskell 的优势!)。
Previous answers all explain the difference quite well.以前的答案都很好地解释了差异。 I just wanted to shed a some light on the inner workings so that it could be easier to understand how liftIO
isn't something magical (for the novice Haskellers like me).我只是想阐明内部工作原理,以便更容易理解liftIO
不是什么神奇的东西(对于像我这样的新手 Haskeller)。
liftIO :: IO a -> m a
is a wise tool just build upon是一个明智的工具,只是建立在
lift :: (Control.Monad.Trans.Class.MonadTrans t, Monad m) => m a -> t m a
and most often used when the bottom monad is IO
.最常用于底层 monad 是IO
。 For the IO
monad it's definition is quite simple.对于IO
monad,它的定义非常简单。
class (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = id
That simple... liftIO
is in fact just id
for the IO
monad and basically IO
is the only one that comes within the definition of the type class.就liftIO
简单…… liftIO
实际上只是IO
monad 的id
并且基本上IO
是类型类定义中唯一的一个。
The thing is, when we have a monad type which is composed of several layers of monad transformers over IO
, we better have an MonadIO
instance for each one of those monad transformer layers.问题是,当我们有一个由IO
几层 monad 转换器组成的 monad 类型时,我们最好为每个 monad 转换器层都有一个MonadIO
实例。 For example the MonadIO
instance of MaybeT m
requires m
to be of MonadIO
typeclass as well.例如, MaybeT m
的MonadIO
实例要求m
也是MonadIO
类型类。
Writing a MonadIO
instance is basically a very simple task too.编写MonadIO
实例基本上也是一项非常简单的任务。 For MaybeT m
it is defined like对于MaybeT m
它定义为
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
or for StateT sm
或者对于StateT sm
instance (MonadIO m) => MonadIO (StateT s m) where
liftIO = lift . liftIO
they are all the same.他们都是一样的。 Imagine when you have a 4 layer transformer stack then you either need to do lift . lift . lift . lift $ myIOAction
想象一下,当你有一个 4 层的变压器堆栈时,你要么需要做lift . lift . lift . lift $ myIOAction
lift . lift . lift . lift $ myIOAction
lift . lift . lift . lift $ myIOAction
or just liftIO myIOAction
. lift . lift . lift . lift $ myIOAction
或只是liftIO myIOAction
。 If you think about it, every lift . liftIO
如果你想一想,每次lift . liftIO
lift . liftIO
will take you one layer down in the stack up until it digs all the way down to IO
at where liftIO
is defined as id
and finalizes with the same code like composed lift
s above. lift . liftIO
将带您在堆栈中向下一层,直到它一直挖掘到IO
,其中liftIO
被定义为id
并使用与上面的组合lift
s 相同的代码完成。
So this is basically why regardless of the transformer stack configuration, provided that all underlaying layers are members of MonadIO
and MonadTrans
a single liftIO
is just fine.所以这基本上就是为什么不管变压器堆栈配置如何,只要所有底层都是MonadIO
和MonadTrans
的成员,一个单一的liftIO
就可以了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.