Continuations in der Praxis
Gewisse Konzepte, die in der funktionalen Programmierung häufig vorkommen, sind in imperativen oder objekt-orientierten Sprachen nahezu unbekannt. Eines dieser Konzepte sind Continuations. Kurz gesprochen ermöglichen Continuations den Zugriff auf den „Rest“ einer Berechnung. Diesen Rest kann man sich dann beispielsweise merken und zu einem späteren Zeitpunkt ausführen.
In diesem Artikel möchte ich Continuations anhand eines praktischen Beispiels erklären. In einem früheren Artikel hatte ich schonmal die Architekur von Checkpad MED vorgestellt, eine elektronische Krankenakte deren Serverkomponente fast vollständig mit Haskell realisiert ist. In dieser Serverkomponente gibt es einen Teil, welcher aus Krankhausdaten die Elemente der Benutzeroberfläche generiert. Nun soll sich natürlich bei Änderung der Krankenhausdaten auch die Benutzeroberfläche ändern. Und genau dabei spielen Continuations eine wichtige Rolle…
Schauen wir uns doch mal zunächst einen kleinen Teil der Dokumentengenerierung an. (Die Dokumentengenerierung ist der Teil der aus Krankenhausdaten Elemente der Benutzeroberfläche erzeugt.)
labDocGen = mkGen "labDocGen" 42 $ \(patId, labId) ->
do Just pat <- getImportData patId
Just lab <- getImportData labId
return $ LabDoc {
title = patName pat ++ " Labor ++ labDate lab
, sections = [renderLabParams (labParams lab)]
}
Die Variable labDocGen
ist ein sogenannter Generator. Ein Generator hat einen
Namen (hier "labDocGen"
) und eine Versionsnummer für den Code (hier 42
).
Die Versionsnummer wird benötigt damit der Generator nicht nur bei
Daten- sonder auch bei Codeänderungen erneut ausgeführt werden kann.
Außerdem hat der Generator natürlich auch noch eine Generierungsfunktion.
Diese nimmt zwei Parameter, wobei patId
die ID eines Patienten und
labId
die eines Laborbefunds ist. Im Rumpf des Generators wird dann
ein LabDoc
erzeugt, welches später in der Patientenakte angezeigt werden
kann. Um an die benötigten Daten zu kommen benutzt der Generator
zweimal die Funktion getImportData
.
Eine wichtige Eigenschaft des obigen Generatorcodes ist, dass sich die Generatorfunktion einzig um das Erzeugen des Ausgabedokuments kümmert. Sie sehen nirgends Logik, die sich um das Neuausführen des Generators bei Änderung der Eingabedaten kümmert.
Die Verantwortung für die Neuausführung der Generatoren bei Code- oder
Datenänderungen liegt einzig beim darunterliegenden Framework. Und genau
an dieser Stelle werden Continuations relevant. Schauen wir uns mal
die Generatorfunktion genauer an. Zuerst holt sie sich mit
getImportData patId
die Daten zum richtigen Patienten. Wenn sich nun
die Daten des Patienten ändern (weil z.B. jemand in der Verwaltung
den Namen des Patienten korrigiert) muss der restliche Code
der Generatorfunktion neu ausgeführt werden.
Was ist denn nun dieser restliche Code? Dazu erinnern wir uns,
dass die do
-Notation in Haskell lediglich eine Kurzschreibweise ist.
Ausgeschrieben sieht der Code des Generators wie folgt aus:
labDocGen = mkGen "labDocGen" 42 $ \(patId, labId) ->
getImportData patId >>= \pat ->
getImportData labId >>= lab ->
return $ LabDoc {
title = patName pat ++ " Labor ++ labDate lab
, sections = [renderLabParams (labParams lab)]
}
Jetzt sieht man deutlich, dass der nach getImportData patId
ausgeführte
Teil des Codes, also die Continuation, alles nach \pat -> ...
ist.
Um also bei Aktualisierung
der unter der patId
abgelegten Patientendaten das Ausgabedokument
aktuell zu halten, muss sich das Framework nur diese Continuation
merken und zu den richtigen Zeitpunkten wieder ausführen. Da die
Generatorfunktion in einer vom Framework bereitgestellten Monade läuft,
ist der Zugriff auf die Continuation auch einfach möglich. Und das
erneute Ausführen der Continuation ist auch einfach, denn schließlich
handelt es sich nur um eine ganz normale Funktion, die einfach nur
mit den geänderten Patientendaten gefütter werden muss.
So, das war‘s für heute. Wir werden uns sicher in einem späteren Artikel Continuations nochmal genauer anschauen und dabei auch kennenlernen, dass Continuations in Haskell auch nur eine Monade sind.
Ich freu‘ mich über Feedback jedweder Art!