Что я могу сделать с callCC, что нельзя сделать с помощью cont?

9

Я действительно очень борюсь с пониманием callCC. Я получаю силу от Continuations и использую концепцию в некоторых своих проектах для создания классных концепций. Но мне никогда не приходилось использовать что-то с большими возможностями, чем cont :: ((a->r)->r)-> Cont r a.

После его использования становится понятным, почему они называют Cont Monad матерью всех монад, ДА, я не понимаю, когда мне нужно будет использовать callCC, и это точно мой вопрос.

Алехандро Навас
источник
Как вы использовали Cont? Когда вы говорите, что вам не нужно использовать что-то более мощное cont, значит ли это, что вы не использовали resetили shiftто и другое?
К. А.
Я не использовал resetили shift. Я использовал его, чтобы определить внедренный язык, который можно приостановить до тех пор, пока данное действие не будет разрешено другим процессом, а затем он возобновится с заданным «продолжением». Может быть, у меня сложилось впечатление, что у меня большой опыт работы с Cont Monad, но на самом деле не так много, я просто очень хочу понять callCC
Алехандро Навас

Ответы:

10

callCC дает вам семантику «раннего возврата», но в монадическом контексте.

Скажем , вы хотите doOne, и если это возвращается True, вы немедленно прекратить, в противном случае вы идете к doTwoи doThree:

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здесь не специальный синтаксис (как returnв C-подобных языках), а просто значение, как у любого другого, вы можете передать его и другим функциям! И эти функции могут затем выполнять то, что называется «нелокальным возвратом» - то есть они могут «останавливать» doThingsвычисления, даже от нескольких вложенных вызовов вглубь. Например, я мог бы выделить проверку doOneрезультата в отдельную функцию, checkOneподобную этой:

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

doThings = callCC \ret -> do
    checkOne ret
    doTwo
    doThree
Федор Сойкин
источник
Я понял! и bв основном это просто подстановочный знак, так что вы можете связать больше продолжений внутри callCC. В любом случае, после retприменения продолжение, созданное вызовом cc, «вернет» все, что было вставлено ret. Это довольно замысловато, но довольно умно, но чрезвычайно мощно. Я не вижу много мест, где использование такой силы не похоже на убийство мухи с помощью
Алехандро Навас
1
@caeus Рад, что я мог помочь. Если вам понравился мой ответ, вы бы приняли его?
Федор Сойкин