我在状态monad中有一堆有状态函数。程序中的某一点需要有一些IO操作,所以我将IO包装在StateT中,获得了这样的一对类型:
mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a
为了简单起见,我不想将IO上下文传递给主要的函数集,我想避免将它们包装在monad堆栈类型中。但是为了从顶层功能中调用它们,我需要一些类似于升力的东西,但我并不是想从内部单子中取出一个值。相反,我想将StateT monad中的状态转换为State monad中的等价物。要做到这一点,我有以下几点:
wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
let (r,s2) = runState f s
put s2
return r
然后习惯于交错,如下所示:
toplevel = do liftIO $ Some IO functions
wrapST $ Some state mutations
liftIO $ More IO functions
....
这似乎是一个相当明显的代码块,所以我想知道这个函数是否有标准名称,它已经在标准库中的某个地方实现了?我试图保持描述简单,但显然这延伸到将一个变换器拉出堆栈,将包装的值转换为变换器类型的表兄弟,跳过堆栈中的下面的monad,然后将结果推回到结束。
答案 0 :(得分:9)
重构代码以使用类型StateT SomeType m a
而不是State SomeType a
可能是个好主意,因为第一个代码与任意monad堆栈兼容。如果您像这样更改它,则不再需要函数wrapST
,因为您可以直接调用有状态函数。
好。假设您有一个函数subOne :: Monad m => State Int Int
:
subOne = do a <- get
put $ a - 1
return a
现在,将此类函数的类型从State SomeType a
更改为StateT SomeType m a
,保留m
。这样,您的函数可以在任何monadic堆栈上工作。对于那些需要IO的函数,您可以指定底部的monad必须是IO:
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
现在,应该可以同时使用这两个功能:
-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
put $ a - 1
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
toZero :: StateT Int IO ()
toZero = do subOne -- A really pure function
printState -- function may perform IO
a <- get
when (a > 0) toZero
PS:我使用的是GHC 7,其中一些库在中途发生了变化,因此在GHC 6上可能会有所不同。
答案 1 :(得分:3)
更直接地回答您的问题:函数hoist
完全按照您更简单的方式执行。用法示例:
import Control.Monad.State
import Data.Functor.Identity
import Control.Monad.Morph
foo :: State Int Integer
foo = put 1 >> return 1
bar :: StateT Int IO Integer
bar = hoist (return . runIdentity) foo
hoist
是MFunctor
类的一部分,其定义如下:
class MFunctor t where
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
大多数monad变形金刚都有这种情况,但不是ContT
。