我真的很难理解callCC。我获得了Continuations的力量,并且在我的一些项目中一直在使用这个概念来创建很酷的概念。但是我从来不需要使用功能比cont :: ((a->r)->r)-> Cont r a更强大的功能。

在使用它之后,为什么他们将Cont Monad称为所有Monad的母亲(是)非常有意义,我不知道何时需要使用callCC,这正是我的问题。

最佳答案

callCC为您提供“尽早返回”的语义,但在单子(monad)上下文中。

假设您想要doOne,并且如果返回True,则立即停止,否则继续进行doTwodoThree:

doOne :: Cont r Bool
doTwo :: Cont r ()
doThree :: Cont r ()

doThings :: Cont r ()
doThings = do
    one <- doOne
    if one
        then pure ()
        else do
            doTwo
            doThree

看到那里的if分支了吗?一个分支不是那么糟糕,可以处理,但是想象一下,您想保释的地方有多个?这变得非常丑陋,很快。

使用callCC,您可以“提前返回”:您在分支点保释,而不必嵌套其余的计算:

doThings = callCC \ret -> do
    one <- doOne
    when one $ ret ()
    doTwo
    doThree

更令人愉快的阅读!

更重要的是,由于这里的ret不是一种特殊的语法(就像C语言中的return一样),而只是一个像其他值一样的值,因此您也可以将其传递给其他函数!然后这些函数可以执行所谓的“非本地返回”-即,即使从多个嵌套调用开始,它们也可以“停止” doThings计算。例如,我可以将对doOne的结果的检查排除在一个单独的函数checkOne中,如下所示:

checkOne ret = do
    one <- doOne
    when one $ ret ()

doThings = callCC \ret -> do
    checkOne ret
    doTwo
    doThree

10-07 22:46