【问题标题】:Jumping forward with the continuation monad继续单子向前跳跃
【发布时间】:2014-10-14 06:07:02
【问题描述】:

可以在带有延续单子的程序中向后跳转:

{-# 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 ()

这不起作用,因为在Control.Monad.Trans.Cont 中定义的延续单子转换器ContT 没有MonadFix (ContT r m) 的实例。有关详细信息,请参阅Levent Erkok's thesis 的第 5.1 节。

有没有一种方法可以在没有值递归的情况下对延续单子进行前向跳转编码?

ContT 的替代定义是否有 MonadFix (ContT r m) 的实例? Magnus Carlsson 有一个 unpublished draft 提出了这样的建议,但我不知道在我的情况下该怎么做。

【问题讨论】:

  • 我不确定是否有可能比@bennofs 的建议做得更好(尽管您可能可以定义一些帮助功能以使其不那么尴尬。)例如,mdo l; x &lt;- lift $ getChar; l &lt;- setjmp; print x 应该做什么,以及你将如何实现它?
  • @ØrjanJohansen:好点子! (1)它应该做什么:应该暂停执行直到x的值准备好print(在你的例子中,它永远不会准备好,所以它永远被暂停)。 (2)我如何实现它:这应该是懒惰的自动。
  • 是否有更完整的示例来说明您要完成的工作?你可以用参数跳回去,也许这对你的任务来说已经足够了?
  • @JohnL:我只想了解延续。我读到延续足以用函数式语言实现 goto 。但是我看不到如何在 Haskell 中前进。请写一个关于“跳跃参数”的答案。
  • 也许你在问Monad Goto。试试这个:hackage.haskell.org/package/GotoT-transformers-1.0.0.1/docs/…

标签: haskell functional-programming monads continuations monad-transformers


【解决方案1】:

如果您将死代码移动到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) 传递回外部代码,以便它可以跳转到那里。

【讨论】:

  • 感谢您的尝试回答,但这里的重点不是死代码(很简单),而是能够在一些代码中跳转。
  • 这怎么不往前跳?它跳转到lift $ putStrLn "This is not executed",在代码中更进一步?
  • @Bob 更新了 question 答案,包括一个为什么不可能的例子
  • 有了你的回答,后面被跳过的代码就没有办法跳回去了。所以它真的是死代码。我想要goto L2: L1: some code; goto END; L2: goto L1; END: return ()
  • 您能否为您的答案配备诸如setjmp 之类的帮助功能,以使向前跳跃看起来不那么尴尬,并且可能像向后跳跃一样可爱?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 2020-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多