显然MonadConts 比普通Monad的 s 更受限制并且提供更多的权力,这要归功于它的callCC. 这意味着它的实例更少,你可以用它做更多的事情。
查看 的已定义实例时MonadCont,看起来那里列出的所有内容都需要一个Cont或ContT一个已经存在的MonadCont实例。这意味着我们必须从一些开始,Cont或者ContT特别是不能IO变成MonadCont.
callCC但是,我认为在上下文中使用是有意义的IO,因此我们可以简化以下内容(根据官方 Hackage 页面 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
以callCC更清洁的方式在 do 块中使用。
当然,要创建IO一个实例,MonadCont我们必须有一些魔法,因为callCCfor 的IO意思是“调用给定的函数,未来的计算指定现实世界中接下来会发生什么”,所以只有解释器或编译器才能真正知道这意味着什么. 另一方面,我没有看到任何理论上的理由表明这是可导入的,因为 Scheme 已经拥有它很长时间了,并且制作这样一个实例根本不需要更改语言。
可能的问题
我能想到的一个因素是 的语义callCC与适当的清理保证相冲突。许多语言都提供了“try...finally”控制以进行适当的清理,C++ 的析构函数也保证了这一点。我不确定 Haskell 中它是什么,但如果callCC可以IO使用它来逃避任何IO需要清理的相关上下文,因此提供 sush 保证将变得不可能,正如您可以看到Ruby 中发生的那样。
意见讨论
@jozefg 的回答非常好。我只想在这里写下我的看法。
确实
MonadCont来自mtl。但这并不意味着 GHC 或其他编译器不能定义 aunsafeCallCC并定义实例,如果MonadCont正确的定义在编译模块的范围内-XIOMonadCont并被设置。我已经谈到了异常安全,看起来很难确定这一点。但是,Haskell 已经有了,在我看来
unsafePerformIO,这基本上比 更不安全。unsafeCallCC在大多数情况下,原因
callCC是过于强大,应尽可能避免。但是,在我看来,延续传递风格可以用来使惰性求值显式化,这有助于更好地理解程序,从而更容易找到可能的优化。当然 CPS 不是MonadCont,但使用它并将深层嵌套的内部函数转换为 do 表示法是一个自然的步骤。