And*_*rew 7 monads haskell state-monad monad-transformers
我在状态monad中有一堆有状态函数.程序中的某一点需要有一些IO操作,所以我将IO包装在StateT中,得到一对这样的类型:
mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a
Run Code Online (Sandbox Code Playgroud)
为了简单起见,我不想将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
Run Code Online (Sandbox Code Playgroud)
然后这用于交错,如下所示:
toplevel = do liftIO $ Some IO functions
wrapST $ Some state mutations
liftIO $ More IO functions
....
Run Code Online (Sandbox Code Playgroud)
它似乎是一个相当明显的代码块,所以我想知道这个函数是否有标准名称,它已经在标准库中的某个地方实现了?我试图保持描述简单,但显然这延伸到将一个变换器拉出堆栈,将包装的值转换为变换器类型的表兄弟,跳过堆栈中的下面的monad,然后将结果推回到结束.
重构代码以使用类型StateT SomeType m a
而不是State SomeType a
,因为第一个与任意monad堆栈兼容,这可能是个好主意.如果您像这样更改它,则不再需要函数wrapST
,因为您可以直接调用有状态函数.
好的.假设你有一个功能subOne :: Monad m => State Int Int
:
subOne = do a <- get
put $ a - 1
return a
Run Code Online (Sandbox Code Playgroud)
现在,改变类型的这样一个从所有功能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
Run Code Online (Sandbox Code Playgroud)
现在,应该可以将两个函数一起使用:
-- 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
Run Code Online (Sandbox Code Playgroud)
PS:我使用GHC 7,一些libs在中途发生了变化,因此在GHC 6上可能会有所不同.