Kontroll- und Datenfluß

Auf den ersten Blick sehen Conditions und Sondervariablen nicht so aus, als hätten sie viel mit einander zu tun. Sondervariablen scheinen eine Mischung aus globalen Variablen und Funktionsargumenten zu sein, während Conditions augenscheinlich Exceptions ähneln. Sieht man aber näher hin, so ergeben sich interessante Parallelen. Sehen wir uns einen einfachen Kontrollfluß zwischen mehreren Prozeduren an, einmal mit einem Zugriff auf eine Sondervariable und einmal mit dem Auslösen einer Condition:

Offenbar passiert hier jeweils Ähnliches. Kein Wunder: Sowohl die Bindung für eine Sondervariable als auch die für einen Condition-Handler befinden sich jeweils im dynamischen Kontext der Ausführung.

Interessant wird es aber dann, wenn wir uns den Datenfluß ansehen:

Hier zeigt sich nämlich ein anderes Bild: Wenn der Datenfluß auch stets von der Seite aus initiiert wird, die tiefer in der Aufrufskette liegt, so zeigt er doch in die je umgekehrte Richtung.

In der Tat kann man den Nutzen von Sondervariablen wie von Conditions so auf den Punkt bringen: Sie ermöglichen eine globale Kommunikation zwischen verschiedenen, unter Umständen weit von einander getrennten Teilen der Aufrufskette. Sondervariablen laufen dabei von oben nach unten, Conditions von unten nach oben, und zwar jeweils ohne Mitwirkung des Codes, der zwischen den Kommunikanten liegt. Auf diese Weise ergänzen die beiden Konzepte gemeinsam das Konzept des Funktionsaufrufs, welches mit seinen Argumenten und Rückgabewerten eine lokale Kommunikation ermöglicht.

Gegenseitige Simulation

Interessant ist auch eine weitere Beobachtung: Conditions und Sondervariablen können einander simulieren.

Zuerst der einfache Fall: Wir simulieren Conditions mithilfe von Sondervariablen. Dabei setzen wir voraus, daß unsere Sprache über Closures sowie — bei Bedarf — ein primitives System zur nichtlokalen Kontrollübergabe (z.B. Exceptions) verfügt. In diesem Fall ist es nämlich denkbar einfach: Wir führen eine Sondervariable ein, in der wir eine Liste der aktiven Condition-Handler speichern. Dabei handelt es sich schlicht um Closures. Die Auslösefunktion für Conditions kann dann dadurch implementiert werden, daß sie durch die Liste der aktiven Handler sieht und den richtigen aufruft. Die nichtlokale Kontrollübergabe kommt dabei nur dann ins Spiel, wenn der Condition-Handler sich dazu entschließt, den Stack abzurollen und den Unteraufruf zu beenden, z.B. weil die Condition für einen irreparablen Fehler steht.

Der umgekehrte Fall ist zugegebenermaßen etwas weiter hergeholt — man könnte auch sagen: an den Haaren herbeigezogen. Dennoch: Wir können für jede Sondervariable, die wir simulieren möchten, eine Condition einführen. An der Stelle der Variablenbindung binden wir stattdessen einen Condition-Handler, der als Rückgabewert den Wert zurückgibt, der an die Variable gebunden werden soll. An die Stelle des Zugriffs auf die Variable setzen wir die Auslösung der Condition. Der Wert der Variablen ist dann der Rückgabewert des Condition-Handlers. (Falls das Condition-System keine Rückgabewerte erlaubt, so kann man diese freilich, wenn auch möglicherweise unter Mehraufwand, mithilfe einer globalen Variablen simulieren.)

Conclusio

Es ist immer wieder faszinierend, wie gut die Teile von Common Lisp zusammenpassen. Das Condition-System ist bereits für sich genommen ein Alleinstellungsmerkmal der Sprache, genau wie das Makrosystem, CLOS, die unheimlich dynamische Entwicklungsweise, und und und — aber nur im größeren Zusammenhang erschließt sich, wie durchdacht das Gesamtsystem eigentlich ist. Daß das Condition-System erst im Angesicht von Sondervariablen zeigt, daß es „paßt“, fügt sich in dieses Schema nur zu gut ein.