显然 MonadCont s 比普通的 Monad s 受到更多限制,并且提供更多功能,这要归功于它的 callCC 。这意味着它的实例更少,你可以用它做更多的事情。

当查看 MonadCont 的定义实例时,看起来那里列出的所有内容都需要 ContContT 或一个已经存在的 MonadCont 实例。这意味着我们必须从一些 ContContT 开始,特别是不能将 IO 变成 MonadCont

但是,我认为在 callCC 上下文中使用 IO 是有意义的,因此我们可以简化以下内容(根据 official Hackage page callCC 示例调整):

whatsYourName :: IO ()
whatsYourName = do
    name <- getLine
    let response = flip runCont id $ do
        callCC $ \exit -> do
            when (null name) (exit "You forgot to tell me your name!")
            return $ "Welcome, " ++ name ++ "!"
    print response

进入
whatsYourName' :: IO ()
whatsYourName' = do
    name <- getLine
    response <- callCC $ \exit -> do
            when (null name) (exit "You forgot to tell me your name!")
            return $ "Welcome, " ++ name ++ "!"
    print response

以更简洁的方式在 do 块中使用 callCC

当然,为了使 IO 成为 MonadCont 的实例,我们必须有一些魔法,因为 callCCIO 意味着“调用给定的函数, future 的计算指定了现实世界中接下来发生的事情”,所以实际上只有解释器或编译器才能知道这是什么意思。另一方面,我没有看到任何理论上的理由表明这是可导入的,因为 Scheme 已经拥有它很长时间了,并且制作这样的实例根本不需要语言更改。

可能的问题

我能想到的一个因素是 callCC 的语义与适当的清理保证冲突。许多语言提供了“try...finally”控制以进行适当的清理,C++ 的析构函数也保证了这一点。我不确定它在 Haskell 中是什么,但是如果 callCC 可用于 IO ,则可以使用它来摆脱任何需要清理的 IO 相关上下文,因此提供 sush 保证将变得不可能,正如您所看到的 what happens in Ruby

意见讨论

@jozefg 的回答非常好。我只想在这里写下我的看法。
  • MonadCont 确实来自 mtl。但这并不意味着 GHC 或其他编译器不能定义 unsafeCallCC 并定义实例,如果具有正确定义的 MonadCont 在编译模块和 -XIOMonadCont 被设置的范围内。
  • 我已经谈过异常安全,但看起来很难确定。但是,Haskell 已经有了 unsafePerformIO ,在我看来,它基本上比 unsafeCallCC 更不安全。
  • 当然,callCC 在大多数情况下过于强大,应尽可能避免使用。然而,在我看来,延续传递风格可以用来使惰性求值显式化,这有助于更好地理解程序,从而更容易找到可能的优化。因为 CPS 不是 MonadCont ,但它是使用它并将深层嵌套的内部函数转换为 do 符号的自然步骤。
  • 最佳答案

    我会说这是一个坏主意。首先,MonadCont 在 MTL 中。 GHC 对此一无所知,这意味着使编译器依赖于 3rd 方库,ick。

    其次,callCC 即使在一些非常高调的 Schemers 中也不受欢迎,主要是因为它使对代码的推理变得痛苦!与 goto 难以推理的方式大致相同。特别是在 Haskell 中,我们必须担心的地方

  • 异常安全吗? (这已经很难了)
  • callCC 安全吗?

  • 最后,我们甚至不需要它。如果您想使用延续和 IO,请使用 ContT IO ,它同样强大。但是,我几乎可以保证它可以用功能较弱的东西替换,例如 monad-prompt 。延续是一把大锤,10 次中有 9 次,callCC 太强大了,可以使用高阶函数和惰性的组合来更愉快地表达。

    例如,callCC 的典型用途之一是实现诸如异常之类的东西,但在 Haskell 中,我们可以只使用 monads :)(它依赖于高阶函数和惰性)。

    从本质上讲,您的提议增加了复杂性,意味着将 MTL 合并到 base 中,以及避免使用 liftIO 的一大堆其他不愉快。

    RE 编辑
  • 当然你可以这样做,但它当然不是 MonadCont 的实例 :)
  • 这有点不同,unsafePerformIO 旨在用于其他人看不到副作用,因为您无法保证事情如何或何时执行。

    如果 callCCIO 就是这种情况,那么您可以只使用 Cont !
  • 延续传递风格很有用,我们有 ConT r IO !这对我来说是棺材上最大的钉子,我认为 没有任何 的好处,而不是仅仅使用现有的库来克服困难且可能不安全的编译器黑客攻击。
  • 关于haskell - 让 IO 成为 MonadCont 的实例有意义吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19851915/

    10-13 00:46