这是ContT的合适用途吗?

时间:2017-07-03 03:37:28

标签: haskell monads continuations

我正在开展一个需要我写一个小翻译的项目。指令具有简单的树结构,其中一个命令具有停止执行的效果。所以在下面的例子中," baz"从未打印过。

import Control.Monad.Cont

data Instruction = Print String | Halt | Block [Instruction]
    deriving (Eq, Show)

instructions =
  [ Print "foo"
  , Block
    [ Print "bar"
    , Halt
    ]
  , Print "baz"
  ]

main :: IO ()
main = runContT (callCC $ interpret instructions)
                (const $ pure ())

interpret []     k = pure ()
interpret (a:as) k = case a of
    Print str -> liftIO (putStrLn str) >> interpret as k
    Block ins -> interpret ins k       >> interpret as k
    Halt      -> k ()

这是我第一次在我的某个项目中看到ContT的潜在用途。我想知道这是否适当使用它,或者是否有一个更简单的解决方案我可能会忽略。

1 个答案:

答案 0 :(得分:8)

是的,这看起来恰恰是Control.Monad.Cont适合的用例类型。

你几乎可以肯定地知道这一点,但是对于其他读者来说,如果你写了interpret以便它是一个从指令列表到一个函数的函数,那么值得说明一下。 IO ()喜欢这样:

main :: IO ()
main = interpret instructions

interpret :: [Instruction] -> IO ()
interpret []     = pure ()
interpret (a:as) = case a of
    Print str -> putStrLn str >> interpret as
    Block ins -> interpret ins >> interpret as
    Halt      -> pure ()

然后foo barbaz都会打印出来。你需要的是一个转义机制,它允许你中止整个计算并立即返回一个值。这正是callCC提供的内容。调用命名计算(代码中的k)可以让您逃脱整个计算,而不仅仅是那个级别/层。

如此出色的工作,我相信,您已经在这里找到了恰当的ContT用例。