可以使用continuation monad在程序中向后跳转:
{-# LANGUAGE RecursiveDo #-}
import Control.Monad.Fix
import Control.Monad.Trans.Cont
setjmp = callCC (\c -> return (fix c))
backward = do
l <- setjmp
-- some code to be repeated forever
l
但是当我试图向前跳时,GHC不接受它:
forward = mdo
l
-- some dead code
l <- setjmp
return ()
这不起作用,因为MonadFix (ContT r m)
中没有ContT
的实例用于Control.Monad.Trans.Cont
中定义的延续monad转换器ContT
。有关详细信息,请参阅Levent Erkok's thesis的第5.1节。
对于continuation monad,有没有办法对没有值递归的前向跳转进行编码?
是否有MonadFix (ContT r m)
的替代定义,其中包含{{1}}的实例?马格努斯·卡尔森(Magnus Carlsson)有一个unpublished draft提出了这样的建议,但我不知道该怎么办。
答案 0 :(得分:7)
如果您将死代码移到callCC
内,可以这样做,如下所示:
import Control.Monad.Cont
forward :: ContT () IO ()
forward = do
callCC $ \skip -> do
skip ()
lift $ putStrLn "This is not executed"
lift $ putStrLn "Control flow continues here"
main :: IO ()
main = runContT forward return
不可能完全按照自己的意愿行事。要了解原因,请考虑以下示例:
mdo
l
c <- lift getChar
l <- if c == 'a' then setjmp else return (return ())
lift $ putStrLn "end"
这应该做什么?
您也可以稍后跳回到已跳过的代码。您只需将延续传递给您跳过的代码即可。使用您的示例,goto L2: L1: some code; goto END; L2: goto L1; END: return
可以实现为:
import Control.Monad.Cont
forward :: ContT () IO ()
forward = do
callCC $ \end -> do
l1 <- callCC $ \l2 -> do
callCC $ \l1 -> l2 l1
liftIO $ putStrLn "In L1"
end ()
liftIO $ putStrLn "In L2"
l1 ()
liftIO $ putStrLn "End"
main :: IO ()
main = runContT forward return
这里我们将延续传递给我们跳过的部分(l1
)回到外部代码,以便它可以跳转到那里。