gpt4 book ai didi

haskell - StateT 超过 Cont。为什么我的状态没有被重置?

转载 作者:行者123 更新时间:2023-12-04 19:29:53 26 4
gpt4 key购买 nike

我在玩Cont描述的单子(monad)技巧herethis SO question .

这个函数让你“跳回”到计算的早期,接受一个参数,这样你就可以做不同的事情:

import Control.Monad.Cont
import Control.Monad.State.Strict
import Control.Monad.Writer.Strict

getCC' :: MonadCont m => a -> m (a,a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))

我在 Cont 之上有这些 monad 转换器的玩具示例:
foo :: WriterT String (Cont String) ()
foo = do
(stop,loop) <- getCC' False
if stop
then do tell "bbb"
else do tell "aaa"
loop True

foo' :: StateT String (Cont String) ()
foo' = do
(stop,loop) <- getCC' False
if stop
then do modify $ \s -> s ++ "bbb"
else do modify $ \s -> s ++ "aaa"
loop True

在第一个示例中(如链接的 SO 问题中的 explained) Cont 的影响对 WriterT 的影响具有“优先级” .当我们重置计算时,日志会丢失:
*Main> print $ runCont (execWriterT foo) id
"bbb"

第二个例子完全一样,只是使用了 StateT。而不是 WriterT .但是,在这种情况下,日志会被保留!
*Main> print $ runCont (execStateT foo' "") id
"aaabbb"

这种差异的解释是什么?

最佳答案

(我觉得这不是一个完全令人满意的答案,但至少应该澄清一点。)

我相信这是因为解除callCC .在 state monad 的情况下,在把兔子追到洞里之后,我们遇到了这个:

liftCallCC :: CallCC m (a, s) (b, s) -> CallCC (StateT s m) a b

Uniform lifting of a callCC operation to the new monad. This version rolls back to the original state on entering the continuation.

liftCallCC' :: CallCC m (a, s) (b, s) -> CallCC (StateT s m) a b

In-situ lifting of a callCC operation to the new monad. This version uses the current state on entering the continuation.



拿的是哪一个?一种保存状态:
instance MonadCont m => MonadCont (LazyState.StateT s m) where
callCC = LazyState.liftCallCC' callCC

instance MonadCont m => MonadCont (StrictState.StateT s m) where
callCC = StrictState.liftCallCC' callCC

writer monad 会发生什么?
instance (Monoid w, MonadCont m) => MonadCont (LazyWriter.WriterT w m) where
callCC = LazyWriter.liftCallCC callCC

instance (Monoid w, MonadCont m) => MonadCont (StrictWriter.WriterT w m) where
callCC = StrictWriter.liftCallCC callCC

啊哈!没有 ' !

liftCallCC :: Monoid w => CallCC m (a, w) (b, w) -> CallCC (WriterT w m) a b

Lift a callCC operation to the new monad.



在库中没有找到保持状态的变体。相反,上面的变体在那里被定义为
liftCallCC callCC f = WriterT $
callCC $ \ c ->
runWriterT (f (\ a -> WriterT $ c (a, mempty)))

注意 mempty .如果我们有一个 get操作,我们可以将“当前状态”存储在那里,这样它就不会在过程中丢失,但是如果我们这样做了,我们将不再处于 writer monad,而是处于状态一。

另请注意,以相反的顺序堆叠 monad 可以达到我们想要的效果。
bar :: ContT String (Writer String) ()
bar = do
(stop,loop) <- getCC' False
if stop
then do lift $tell "bbb"
else do lift $ tell "aaa"
loop True

-- > runWriter (runContT bar (const $ pure ""))
-- ("","aaabbb")

关于haskell - StateT 超过 Cont。为什么我的状态没有被重置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44988528/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com