【问题标题】:Escaping from the IO monad inside the Continuation monad从 Continuation monad 中的 IO monad 转义
【发布时间】:2011-09-29 15:21:29
【问题描述】:

一个令人困惑的问题的令人困惑的标题!我理解 a) monad,b) IO monad,c) Cont monad (Control.Monad.Cont),以及 d) ContT 延续转换器 monad。 (而且我对一般的 monad 转换器有模糊的了解——尽管不足以回答这个问题。)我了解如何编写一个程序,其中 所有 函数都在 Cont monad (Cont r a) 中,并且我了解如何编写一个程序,其中 所有 函数都在组合的 Cont/IO monad (ContT r IO a) 中。

但我想知道如何编写一个程序,其中 some 函数位于组合的 Cont/IO monad (ContT r IO a) 和 other 函数中Cont monad (Cont r a)。基本上,我想以延续风格编写整个程序,但只在必要时使用 IO monad(就像在“常规”Haskell 代码中一样,我只在必要时使用 IO monad)。

例如考虑这两个函数,以非延续样式:

foo :: Int -> IO Int
foo n = do
    let x = n + 1
    print x
    return $ bar x

bar :: Int -> Int
bar m = m * 2

请注意,foo 需要 IO,但 bar 是纯的。现在我想出了如何完全使用 continuation monad 编写这段代码,但我还需要通过 bar 线程化 IO:

foo :: Int -> ContT r IO Int
foo n = do
    let x = n + 1
    liftIO $ print x
    bar x

bar :: Int -> ContT r IO Int
bar m = return $ m * 2

确实希望我的所有代码都采用延续样式,但我希望必须在不需要它的函数上使用 IO monad。基本上,我希望这样定义bar

bar :: Int -> Cont r Int
bar m = return $ m * 2

不幸的是,我找不到从 ContT r IO a monad 函数 (foo) 内部调用 Cont r a monad 函数 (bar) 的方法。有没有办法将未转换的单子“提升”为已转换的单子?即,如何更改foo 中的“bar x”行,以便它可以正确调用bar :: Int -> Cont r Int

【问题讨论】:

    标签: haskell monads continuations


    【解决方案1】:

    这就是 Control.Monad.Class 的用武之地。使 bar 在它可以工作的 monad 中具有多态性:

    bar :: MonadCont m => Int -> m Int
    bar m = return $ m * 2
    

    请注意,页面底部的实例列表显示生成文档时已知的MonadCont 实例包括Cont rMonad m => ContT r m。此外,MonadCont 类定义了 callCC 函数,这是使用延续功能所必需的。这意味着您可以在 bar 中使用延续的完整表达能力,即使此示例没有。

    这样,您编写的函数可能无法使用 IO,因为它们没有MonadIO 约束,也没有明确提及IO。但是它们在其中工作的 monad 是多态的,因此可以从包含 IO 的上下文中轻松调用它们。

    【讨论】:

    • 谢谢。这样可行。我还找到了自己的解决方案,这正是我想要的(我不必更改Bar):liftCont :: Cont (m r) a -> ContT r m a; liftCont c = ContT $ runCont c。我的解决方案解包Cont 并构造ContT。我认为您的解决方案更好,因为它是多态的并且不需要对数据结构进行实际操作,所以请为您打勾。但我会将我的作为另一个答案发布,因为如果您无法修改bar,它会很有帮助。另外 +1 用于解释为什么在 bar 中无法使用 IO。
    【解决方案2】:

    我发现这正是我想要的(无需更改 Bar):

    liftCont :: Cont (m r) a -> ContT r m a
    liftCont = ContT . runCont
    

    这将解包Cont 并构造一个ContT

    然后我可以使用liftContFoo 调用Bar

    foo n = do
        let x = n + 1
        liftIO $ print x
        liftCont $ bar x
    

    我不认为这比 Carl 的解决方案“更好”(我给了他打勾),但我在这里发布它是因为它允许您使用 Bar 而无需修改其类型,如果您无法修改它非常有用Bar。 (不过它的性能可能更差。)

    【讨论】:

      【解决方案3】:

      另一种选择是考虑 mmorph 包 https://hackage.haskell.org/package/mmorph-1.0.0/docs/Control-Monad-Morph.html#v:hoist

      在教程部分,看看hoist可以做什么。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-27
        • 1970-01-01
        • 2015-03-01
        • 1970-01-01
        • 2017-12-06
        • 1970-01-01
        • 1970-01-01
        • 2020-03-26
        相关资源
        最近更新 更多