显然,由于其 callCC
,MonadCont
比普通 Monad
受到更多限制,并且提供更多功能。这意味着它的实例更少,您可以用它做更多的事情。
当查看 MonadCont
的定义实例时,看起来那里列出的所有内容都需要 Cont
或 ContT
或已经存在的 MonadCont
实例。这意味着我们必须从一些 Cont
或 ContT
开始,特别是不能将 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 的回答非常好。我只是想在这里写下我的看法。
MonadCont
确实来自 mtl。但这并不意味着 GHC 或其他编译器无法定义unsafeCallCC
并定义实例(如果具有正确定义的MonadCont
位于编译模块和-XIOMonadCont 的范围内)
正在设置。我已经讨论过异常安全性,但看起来很难确定这一点。然而,Haskell 已经有了
unsafePerformIO
,在我看来,它基本上比unsafeCallCC
更不安全。当然,在大多数情况下,
callCC
过于强大,应尽可能避免。然而,在我看来,连续传递风格可以用来使惰性求值显式化,这可以帮助更好地理解程序,从而更容易找到可能的优化。当然,CPS 不是MonadCont
,但使用它并将深层嵌套内部函数转换为 do 表示法是一个自然的步骤。
最佳答案
我想说这是一个坏主意。首先,MonadCont
位于 MTL 中。 GHC对此一无所知,这意味着让编译器依赖于第3方库,ick。
其次,callCC
即使在一些非常知名的策划者中也不受欢迎,主要是因为它使代码推理变得很痛苦!就像 goto
很难推理一样。特别是在 Haskell 中我们必须关心的地方
- 异常安全吗? (这已经很难了)
- callCC 安全吗?
最后,我们甚至不需要它。如果您想使用延续和 IO,请使用 ContT IO,它同样强大。但是,我几乎可以保证它可以被替换为功能较弱的东西,例如 monad-prompt 。延续是一把大锤,十次中有九次,callCC
太强大了,可以使用高阶函数和惰性的组合来更愉快地表达。
举个例子,callCC
的典型用途之一是实现异常之类的东西,但在 Haskell 中我们可以只使用 monad :)(它依赖于高阶函数和惰性)。
本质上,您的提议增加了复杂性,意味着将 MTL 合并到基础中,以及一大堆其他令人不快的事情,以避免 liftIO
。
RE 编辑
- 当然可以这样做,但它当然不是
MonadCont
的实例:) 这有点不同,
unsafePerformIO
的目的是让其他人看不到副作用,因为您无法保证如何或何时执行事物。如果
callCCIO
就是这种情况,那么您只需使用Cont
!连续传递风格很有用,我们已经通过
ConT r IO
实现了!这对我来说是棺材上最大的钉子,我不认为这个想法比仅使用现有库而不是困难且可能不安全的编译器黑客有任何好处。
关于haskell - 让 IO 成为 MonadCont 的实例有意义吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19851915/