Während ich versuchte, etwas Intuition für den ContT-Monade-Transformer aufzubauen, fand ich mich (vielleicht nicht überraschend) verwirrt. Das Problem liegt in der shiftT-Operation, die nichts Nützliches zu tun scheint.
Zuerst ein einfaches Beispiel, wie man es benutzen könnte
%Vor%%code% könnte ein etwas komplexerer Ausdruck sein, solange er %code% zurückgibt. Nun, ein Versuch, meine Intuition zu erklären, dass shiftT ist, fügt nichts hinzu:
%Vor%Es stellte sich heraus, dass wir ContT direkt erstellen könnten.
Fragestunde: Gibt es eine Situation, in der shift / shiftT etwas über cont / ContT hinzufügt? Oder werden sie nur verwendet, um den Code lesbarer zu machen?
Nach dem Suche Github von gurkenglas 's Rat, den ich diese sehr schöne Erklärung von %code% und %code% mit Anwendungsbeispielen, Motivation und Semantik!
Diese Funktionen sind sehr einfach. Ihre Definition in %code% Bibliothek ist einfach:
%Vor%Aber Philosophie und Bedeutung liegen weit hinter einem intuitiven Verständnis. Daher empfehle ich Ihnen, die Erklärung über den obigen Link zu lesen. Manchmal passiert es, dass Dinge, die einfach zu definieren sind, etwas Komplexes tun können.
Angepasste Dokumentation aus der Erklärung in oben verlinkten Pugs:
%code%
%code% ist wie %code% , außer dass Sie die Fortsetzung aktivieren von %code% zur Verfügung gestellt, wird es bis zum Ende des nächsten einschließenden %code% laufen, Springe dann zurück zu dem Punkt, an dem du die Fortsetzung aktiviert hast. Beachten Sie, dass die Kontrolle schließlich zu dem Punkt nach dem zurückkehrt Subkontinuation ist aktiviert, Sie können es mehrmals in der aktivieren gleicher Block. Dies ist anders als in den Fortsetzungen von %code% , die den aktuellen Wert verwerfen Ausführungspfad bei Aktivierung.
Siehe %code% für ein Beispiel, wie diese Begrenzungsunterteilungen tatsächlich aussehen arbeiten.
%code%
Erstellen Sie einen Gültigkeitsbereich, für den %code% die letzten Unterteilungen garantiert Beenden Sie das Ende von. Betrachten Sie dieses Beispiel:
%Vor%Dies wird:
- aus
Führe %code%
- aus
Führe %code%
- aus
Führe %code%
- aus
Binden Sie %code% an 1 und führen Sie daher %code%
- zurück
Fällt vom Ende von %code% und springt direkt nach %code%
- aus
Führe %code%
- aus
Binden Sie %code% an 2 und führen Sie daher %code%
- zurück
Fällt vom Ende von %code% und springt direkt nach %code%
Escape von %code% , was dazu führt, dass es 0 ergibt
Im Gegensatz zu den Fortsetzungen von %code% werden diese Unterkontinuationen schließlich zurück zum Punkt, nachdem sie aktiviert wurden, nachdem sie vom Ende des nächste %code% .
Sie haben Recht, dass Fortsetzungen begrenzt undefinierten oder unzureichend abgegrenzten Fortsetzungen mit ausgedrückt werden kann. Daher können die Definitionen von %code% und %code% immer nur mit %code% beschrieben werden. Aber:
Im Wesentlichen ermöglichen Fortsetzungen, ein Programm umzudrehen: Der durch %code% abgegrenzte Block wird innerhalb des inneren Teils des Programms gequetscht, wenn %code% die übergebene Funktion aufruft. (Im Falle von undefinierten Fortsetzungen ist der gesamte Ausführungskontext drinnen gedrängt, was sie so komisch macht.)
Lassen Sie uns ein paar Beispiele machen:
%Vor%Wenn wir %code% ohne %code% haben, ist es nur eine reine Berechnung, nichts Besonderes. Die obige Funktion gibt einfach %code% zurück.
Nun können wir beide verwenden:
%Vor%Das wird interessanter. Der Code zwischen %code% und %code% wird tatsächlich in die Aufrufe von %code% gedrückt, in diesem einfachen Beispiel ist es nur %code% . Wenn wir %code% aufrufen, wird die gesamte Berechnung durchgeführt und ihr Ergebnis wird das Ergebnis des Aufrufs %code% . Wir machen das zweimal, also rufen wir im Wesentlichen alles zwischen %code% und %code% zweimal auf. Und das Ergebnis der gesamten Berechnung ist %code% , das Ergebnis des Aufrufs %code% .
So in einem Sinn, der %code% Block wird der äußere Teil der Berechnung und der Block zwischen %code% und %code% wird der innere Teil der Berechnung.
So weit, so gut. Aber es wird noch entmutigender, wenn wir %code% zweimal aufrufen, wie in diesem Codebeispiel:
%Vor%Und hier ist, was es produziert (versteckt für diejenigen, die versuchen wollen, es als Übung herauszufinden):
%code%
Nun passiert das Programm zweimal :
Also lassen wir in %code% den Rest der Berechnung für jedes der drei Argumente laufen, und während jedes von ihnen machen wir eine ähnliche Sache für jedes der anderen drei Argumente. Das Ergebnis ist dann das kartesische Produkt der beiden Listen, da wir im Wesentlichen zwei verschachtelte Schleifen durchgeführt haben.
Das gleiche gilt für %code% und %code% , nur mit zusätzlichen Nebenwirkungen. Wenn wir zum Beispiel debuggen wollen, was tatsächlich passiert, können wir den obigen Code in den %code% monad- und print-Debugging-Anweisungen ausführen:
%Vor%