问题描述
我不确定如何科学准确地提出这个问题,所以我只是要向你展示一个例子。
I am not sure how to formulate this question scientifically exact, so I am just going to show you an example.
我在 StateT
变压器中使用状态。底层是 IO
。在 StateT IO
操作中,我需要使用 alloca
。但是,我不能提取 alloca
到 StateT IO
,因为它期望类型为的参数。 (Ptr a - > IO a)
,而我要求它使用参数(Ptr a - > StateT IO MyState a)
。
I am using state in a StateT
transformer. Underlying is IO
. Inside the StateT IO
operation I need to use alloca
. However, I can't lift alloca
to StateT IO
because it expects an argument of type (Ptr a -> IO a)
while I require it to work with an argument of (Ptr a -> StateT IO MyState a)
.
(然而,这是一个关于monad转换器的通用问题,而不是特定于 IO
, StateT
或 alloca
。)
(However, this is a generic question about monad transformers rather than specific to IO
, StateT
or alloca
.)
解决方案:
I came up with the following, working solution:
-- for reference
-- alloca :: (Storable a) => (Ptr a -> IO b) -> IO b
allocaS :: (Storable a) => (Ptr a -> StateT s IO b) -> StateT s IO b
allocaS f = do
state <- get
(res, st) <- liftIO $ alloca $ \ptr -> (runStateT (f ptr) state)
put st
return res
然而,我认为我应该不得不重构和重构 StateT
动作,以便将它与 alloca 。此外,我已经多次看到这种模式的一些变化,并不总是像
StateT
那样简单和安全。
However, it seems wrong to me that I should have to de- and reconstruct the
StateT
action in order to use it with alloca
. Also, I have seen this pattern in some variations more than once and it's not always as simple and safe as here with StateT
.
有没有更好的方法来做到这一点?
Is there a better way to do this?
推荐答案
这可以使用中的code> ,已设计正是为此:
This can be accomplished using
MonadBaseControl
in monad-control, which has been devised exactly for this purpose:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Control.Monad.Trans.Control
import qualified Foreign.Ptr as F
import qualified Foreign.Marshal.Alloc as F
import qualified Foreign.Storable as F
alloca :: (MonadBaseControl IO m, F.Storable a) => (F.Ptr a -> m b) -> m b
alloca f = control $ \runInIO -> F.alloca (runInIO . f)
这个增强版本的
alloca
可以用于任何实现
MonadBaseControl
的基于 IO
的monad堆栈,包括 StateT s IO
。
This enhanced version of
alloca
can be used with any monad stack based on IO
that implements MonadBaseControl
, including StateT s IO
.
MonadBaseControl
的实例允许其monadic值为编码在基本monad中(这里 IO
),传递给基本monad中的函数(如 F.alloca
)然后重新构建它们。
Instances of
MonadBaseControl
allow their monadic values to be encoded in the base monad (here IO
), passed to a function in the base monad (like F.alloca
) and then reconstruct them back.
另请参阅
套餐包含许多标准的
IO
函数提升到 MonadBaseControl IO
,但是 alloca
不在(其中)。
Package lifted-base contains many of the standard
IO
functions lifted to MonadBaseControl IO
, but alloca
isn't (yet) among them.
这篇关于将函数及其参数提升到不同的单一上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!