Apa yang bisa saya lakukan dengan callCC yang tidak dapat dilakukan dengan cont?

9

Saya benar-benar berjuang dengan memahami callCC. Saya mendapatkan kekuatan Kelanjutan dan saya telah menggunakan konsep di beberapa proyek saya untuk membuat konsep keren. Tetapi saya tidak pernah perlu menggunakan sesuatu dengan kemampuan yang lebih besar dari itu cont :: ((a->r)->r)-> Cont r a.

Setelah menggunakannya, masuk akal mengapa mereka menyebut Cont Monad ibu dari semua monad, BELUM saya tidak mengerti kapan saya harus menggunakannya callCC, dan itulah pertanyaan saya.

Alejandro Navas
sumber
Bagaimana Anda menggunakannya Cont? Ketika Anda mengatakan Anda tidak perlu menggunakan sesuatu yang lebih kuat dari itu cont, apakah itu berarti Anda belum pernah menggunakan resetatau shiftkeduanya?
KA Buhr
Saya belum pernah menggunakan resetatau shift. Saya telah menggunakannya untuk mendefinisikan bahasa eembedded yang dapat ditangguhkan sampai tindakan yang diberikan diselesaikan oleh proses lain, dan kemudian dilanjutkan dengan "kelanjutan" yang diberikan. Mungkin saya memberi kesan memiliki banyak pengalaman dengan Cont Monad, tetapi tidak terlalu banyak, saya hanya benar-benar ingin memahami callCC
Alejandro Navas

Jawaban:

10

callCC memberi Anda semantik "kembali awal", tetapi dalam konteks monadik.

Katakanlah Anda ingin doOne, dan jika itu kembali True, Anda segera berhenti, jika tidak Anda pergi ke doTwodan 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

Lihat ifcabang itu di sana? Satu cabang tidak seburuk itu, bisa diatasi, tetapi bayangkan ada beberapa titik di mana Anda hanya ingin menebus? Ini menjadi sangat buruk dengan sangat cepat.

Dengan callCCAnda dapat memiliki "pengembalian awal": Anda membayar pada titik percabangan dan tidak harus membuat sarang dari sisa perhitungan:

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

Jauh lebih menyenangkan untuk dibaca!

Lebih penting lagi, karena di retsini bukan sintaks khusus (seperti returndalam bahasa C-like), tetapi hanya nilai seperti yang lain, Anda dapat meneruskannya ke fungsi lain juga! Dan fungsi-fungsi itu kemudian dapat melakukan apa yang disebut "pengembalian non-lokal" - yaitu mereka dapat "menghentikan" doThingsperhitungan, bahkan dari beberapa panggilan bersarang jauh. Sebagai contoh, saya dapat memfaktorkan pengecekan doOnehasil ke fungsi terpisah checkOneseperti ini:

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

doThings = callCC \ret -> do
    checkOne ret
    doTwo
    doThree
Fyodor Soikin
sumber
Saya mengerti! dan bpada dasarnya hanya wildcard sehingga Anda dapat membuat rantai lebih banyak kelanjutan di dalam callCC. Bagaimanapun, setelah retditerapkan, kelanjutan yang dihasilkan oleh panggilan cc akan "mengembalikan" apa pun yang dimasukkan ret. Itu cukup berbelit-belit, namun cukup pintar, namun sangat kuat. Saya melihat tidak banyak tempat di mana menggunakan kekuatan seperti itu tidak seperti membunuh seekor lalat dengan senjata nuklir
Alejandro Navas
1
@caeus Senang saya bisa membantu. Jika Anda menyukai jawaban saya, apakah Anda akan mempertimbangkan menerimanya?
Fyodor Soikin