haskell - 让 IO 成为 MonadCont 的实例有意义吗?

标签 haskell monads continuation

显然,由于其 callCCMonadCont 比普通 Monad 受到更多限制,并且提供更多功能。这意味着它的实例更少,您可以用它做更多的事情。

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

但是,我相信在 IO 上下文中使用 callCC 是有意义的,因此我们可以简化以下内容(根据 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 block 中使用 callCC

当然,要使 IO 成为 MonadCont 的实例,我们必须有一些魔法,因为 callCC 对于 IO > 表示“使用 future 计算调用给定函数指定现实世界中接下来发生的事情”,因此只有解释器或编译器才能真正知道这意味着什么。另一方面,我没有看到任何理论上的原因表明这是重要的,因为Scheme已经拥有它很长时间了,并且创建这样的实例根本不需要改变语言。

可能的问题

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

意见讨论

@jozefg 的回答非常好。我只是想在这里写下我的看法。

  1. MonadCont 确实来自 mtl。但这并不意味着 GHC 或其他编译器无法定义 unsafeCallCC 并定义实例(如果具有正确定义的 MonadCont 位于编译模块和 -XIOMonadCont 的范围内) 正在设置。

  2. 我已经讨论过异常安全性,但看起来很难确定这一点。然而,Haskell 已经有了 unsafePerformIO,在我看来,它基本上比 unsafeCallCC 更不安全。

  3. 当然,在大多数情况下,callCC 过于强大,应尽可能避免。然而,在我看来,连续传递风格可以用来使惰性求值显式化,这可以帮助更好地理解程序,从而更容易找到可能的优化。当然,CPS 不是 MonadCont,但使用它并将深层嵌套内部函数转换为 do 表示法是一个自然的步骤。

最佳答案

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

其次,callCC 即使在一些非常知名的策划者中也不受欢迎,主要是因为它使代码推理变得很痛苦!就像 goto 很难推理一样。特别是在 Haskell 中我们必须关心的地方

  1. 异常安全吗? (这已经很难了)
  2. callCC 安全吗?

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

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

本质上,您的提议增加了复杂性,意味着将 MTL 合并到基础中,以及一大堆其他令人不快的事情,以避免 liftIO

RE 编辑

  1. 当然可以这样做,但它当然不是 MonadCont 的实例:)
  2. 这有点不同,unsafePerformIO 的目的是让其他人看不到副作用,因为您无法保证如何或何时执行事物。

    如果 callCCIO 就是这种情况,那么您只需使用 Cont!

  3. 连续传递风格很有用,我们已经通过 ConT r IO 实现了!这对我来说是棺材上最大的钉子,我不认为这个想法比仅使用现有库而不是困难且可能不安全的编译器黑客有任何好处。

关于haskell - 让 IO 成为 MonadCont 的实例有意义吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19851915/

相关文章:

C# 等待与延续 : not quite the same?

haskell - 试图将 CPS 应用于口译员

haskell - 如何在 Haskell 中编写错误类型?

haskell - 如何将 MonadLogger 添加到我的免费 monad 转换器堆栈中?

Haskell 左箭头运算符替代

list - 如何将 all 与 monadic 函数一起使用?

haskell - 将类型定义为 Monad

c# - 当数组中的一个或多个任务被取消或失败时继续?

haskell - 是否有不能遵守法律的仿函数?

haskell - 如何使用 Template Haskell 检查多态类型的实例是否存在?