¿Qué puedo hacer con callCC que no se puede hacer con cont?

9

Estoy realmente luchando por entender callCC. Obtengo el poder de las Continuaciones y he estado usando el concepto en algunos de mis proyectos para crear conceptos geniales. Pero nunca he necesitado usar algo con mayores capacidades que cont :: ((a->r)->r)-> Cont r a.

Después de usarlo, tiene mucho sentido por qué llaman a Cont Monad la madre de todas las mónadas, PERO, no entiendo cuándo necesitaría usar callCC, y esa es exactamente mi pregunta.

Alejandro Navas
fuente
¿Cómo se ha utilizado Cont? Cuando dices que no has necesitado usar algo más poderoso que conteso, ¿eso significa que no has usado reseto shifttampoco?
KA Buhr
No he usado reseto shift. Lo he usado para definir un lenguaje eembedded que se puede suspender hasta que una acción determinada se resuelva mediante otro proceso, y luego se reanuda con la "continuación" dada. Tal vez doy la impresión de tener mucha experiencia con Cont Monad, pero no tanto, solo quiero entender callCC
Alejandro Navas el

Respuestas:

10

callCC le ofrece una semántica de "retorno temprano", pero en un contexto monádico.

Digamos que quería hacerlo doOne, y si eso regresa True, se detiene inmediatamente, de lo contrario continúa doTwoy 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

¿Ves esa iframificación allí? Una rama no es tan mala, podría tratarse, pero ¿imagina que hay múltiples puntos en los que solo desea rescatar? Esto se pone muy feo muy rápido.

Con callCCusted puede tener un "retorno temprano": puede pagar bajo fianza en el punto de ramificación y no tiene que anidar el resto del cálculo:

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

Mucho más agradable de leer!

Más importante aún, dado que retaquí no hay una sintaxis especial (como returnen los lenguajes tipo C), sino solo un valor como cualquier otro, ¡también puede pasarla a otras funciones! Y esas funciones pueden realizar lo que se llama "retorno no local", es decir, pueden "detener" el doThingscálculo, incluso desde múltiples llamadas anidadas profundas. Por ejemplo, podría factorizar la comprobación del doOneresultado en una función separada checkOnecomo esta:

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

doThings = callCC \ret -> do
    checkOne ret
    doTwo
    doThree
Fyodor Soikin
fuente
¡Lo entiendo! y bes básicamente solo un comodín para que pueda encadenar más continuaciones dentro de callCC. De todos modos, una vez que retse aplica, la continuación producida por la llamada cc "devolverá" lo que se haya introducido ret. Eso es bastante complicado, pero bastante inteligente, pero extremadamente poderoso. No veo muchos lugares donde usar ese poder no sea como matar una mosca con una bomba nuclear
Alejandro Navas,
1
@ Caeus Me alegro de poder ayudar. Si te gustó mi respuesta, ¿considerarías aceptarla?
Fyodor Soikin, el