dan*_*iaz 5 monads continuations haskell monad-transformers
此功能允许您"跳回"到计算中的早期,获取参数,以便您可以采取不同的方式:
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))
Run Code Online (Sandbox Code Playgroud)
我有这些monad变换器的玩具示例Cont:
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
Run Code Online (Sandbox Code Playgroud)
在第一个例子中(如链接的SO问题中所解释的那样)Cont具有"优先级"的效果而不是效果WriterT.当我们重置计算时,日志丢失:
*Main> print $ runCont (execWriterT foo) id
"bbb"
Run Code Online (Sandbox Code Playgroud)
第二个例子做了完全相同的事情,只使用StateT而不是WriterT.但是,在这种情况下,日志会被保留!
*Main> print $ runCont (execStateT foo' "") id
"aaabbb"
Run Code Online (Sandbox Code Playgroud)
这种差异的解释是什么?
(我觉得这不是一个完全令人满意的答案,但至少应该澄清一点。)
我相信这是因为callCC. 在 state monad 的情况下,在把兔子追到洞里之后,我们遇到了这个:
Run Code Online (Sandbox Code Playgroud)liftCallCC :: CallCC m (a, s) (b, s) -> CallCC (StateT s m) a b将 callCC 操作统一提升到新的 monad。此版本在进入延续时回滚到原始状态。
Run Code Online (Sandbox Code Playgroud)liftCallCC' :: CallCC m (a, s) (b, s) -> CallCC (StateT s m) a b将 callCC 操作原位提升到新的 monad。此版本在进入延续时使用当前状态。
拿的是哪一个?一种保存状态:
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
Run Code Online (Sandbox Code Playgroud)
作家 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
Run Code Online (Sandbox Code Playgroud)
啊哈!不'!
Run Code Online (Sandbox Code Playgroud)liftCallCC :: Monoid w => CallCC m (a, w) (b, w) -> CallCC (WriterT w m) a b将 callCC 操作提升到新的 monad。
在库中找不到状态保留变体。相反,上面的变体在那里被定义为
liftCallCC callCC f = WriterT $
callCC $ \ c ->
runWriterT (f (\ a -> WriterT $ c (a, mempty)))
Run Code Online (Sandbox Code Playgroud)
请注意mempty. 如果我们有一个get操作,我们可以在那里存储“当前状态”,这样它就不会在过程中丢失,但是如果我们有的话,我们将不再处于 writer monad 中,而是处于 state 中。
还要注意,以相反的顺序堆叠 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")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
108 次 |
| 最近记录: |