Begriff
Delimited Continuations
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Normale Continuations (call/cc) erfassen den ganzen Rest des Programms (bis Prozess-Ende).
Das ist oft zu viel (Memory Leaks, globaler GOTO).
Delimited Continuations erfassen nur einen Teil des Programms.
Du setzt Marker: reset { ... shift k ... }.
shift schneidet den Stack ab bis zum reset.
Diesen Schnipsel (k) kannst du wie eine Funktion benutzen.
Du kannst ihn 0-mal aufrufen (Code löschen), 1-mal (normal) oder 100-mal (Loop).
Das ist die mächtigste Control-Flow-Primitive, die wir kennen.
Daraus kann man alles bauen: for-Loops, Exceptions, Async/Await, Prolog-Suche.
Merksatz: Eine Erweiterung von Continuations, die es erlaubt, nur einen abgegrenzten Teil des Aufrufstacks (Continuation) zu erfassen, zu speichern und als Funktion wiederzuverwenden (Reifizierung des Kontrollflusses).
In React Fiber! React nutzt intern Konzepte ähnlich wie Delimited Continuations (Suspense), um das Rendering zu pausieren (wenn Daten laden) und später fortzusetzen. In Scala (CPS Plugin) oder Racket. Moderne "Algebraic Effects" Systeme nutzen dies, um Side-Effects testbar zu machen.
1. Shift & Reset
Die Operatoren.
reset: Markiert den Startpunkt ("Hier fangen wir an aufzunehmen").
shift k: Stoppt die Ausführung. k ist der Code zwischen shift und reset.
Beispiel: 1 + reset (2 * shift k -> k(k(5))).
Stack capture: 2 * [].
Wir rufen k(k(5)) auf. k(5) -> 2*5 = 10.
k(10) -> 2*10 = 20.
Ergebnis: 1 + 20 = 21.
2. Typisierung
Delimited Continuations statisch zu typisieren, ist extrem schwer.
Der Typ ändert sich ("Answer Type Modification").
Bevor du shift rufst, erwartete der Kontext einen Int.
Danach erwartet er vielleicht einen String.
Deshalb gibt es das kaum in Java/C++.
1. Delimited vs. Undelimited (The Performance Gap)
Undelimited Continuations (call/cc) müssen oft den kompletten Stack auf den Heap kopieren. Das ist bei tiefen Aufrufkaskaden extrem langsam.
Delimited Continuations sind viel performanter, da der Compiler (oder die Runtime) genau weiß, wo der Frame endet (reset).
In vielen Implementierungen kann ein shift einfach durch das Verschieben eines Stack-Pointers und das Kopieren einiger weniger Frames realisiert werden. Zudem erlauben sie eine bessere Garbage Collection, da die erfassten Fragmente (k) oft kurzlebig sind und keine Referenzen auf den "gesamten Weltzustand" halten.
2. Multi-Prompt Continuations
In der Forschung nutzt man oft mehrere "Prompts" (Marker).
Anstatt nur eines reset, kann man verschiedene Kanäle aufmachen:
prompt A { prompt B { ... shift(A) ... } }.
Dies erlaubt es, verschiedene "Effekte" unabhängig voneinander zu steuern. Beispielsweise könnte Prompt A für das Error-Handling zuständig sein und Prompt B für die Datenbank-Transaktionen. Dies ist die mathematische Grundlage für Algebraic Effects, die derzeit als Nachfolger von Monaden für das Management von Side-Effects in Sprachen wie Koka oder Eff gehandelt werden.
3. Reification of Control Flow
Was bedeutet "Reifizierung" hier? Es bedeutet, dass das "Was der Computer als nächstes tut" nicht mehr tief in der CPU-Hardware verborgen ist, sondern als First-Class Objekt (eine Funktion k) in deinem Quellcode auftaucht.
Du kannst dieses k in ein Array pushen, es serialisieren und über das Netzwerk an einen anderen Server schicken. Diese "Mobile Continuations" ermöglichen es, einen laufenden Task auf Server A zu pausieren und auf Server B fortzusetzen – eine Technik, die für massiv parallele Berechnungen oder "Long-running Workflows" extrem mächtig ist.
Quick-Check
Wozu im Alltag?
Webserver (Request Scope). Ein Request pausiert (wartet auf DB), der Thread wird frei für andere. Später wird der Request fortgesetzt. Das ist im Prinzip Delimited Continuation.Mächtiger als Monaden?
Genauso mächtig (man kann jede Monade damit implementieren:filinski-representable). Aber oft performanter und besser komponierbar.Gehirn-Knoten?
Ja. Shift/Reset zu verstehen gilt als "Schwarzer Gürtel" der funktionalen Programmierung.