UP | HOME

Was ist eine Closure? Oder: Die Blöden da oben!

Closure bedeutet soviel wie Beschluß, Schließung. Closures gab es zuerst in Lisp und mittlerweile findet man sie in immer mehr Programmiersprachen. Kein moderner Sprachentwurf verzichtet heute auf sie. Java wartet noch (bis Version 8 oder 9 oder 10), javascript, perl und sogar Visual Basic kennen sie längst, in Haskell kann man ohne Closures fast nichts machen und Lisp wäre ohne sie nicht halb so elegant.

Inhaltsverzeichnis

1 Die Gesellschafterversammlung

Stellen sie sich vor, Sie sitzen in einer Geschäftsleiterbesprechung. Es ist eine heftige Debatte im Gange und schließlich folgt die Einlassung des Hauptgesellschafters: "Wir setzen diesem Treiben einfach ein Ende, weil es, wie wir erlebt haben, schlecht für unser Betriebsklima ist". Das Gremium entscheidet sich dafür, nun darüber abzustimmen und einen Beschluß zu fassen. Es erfolgt einstimmige Zustimmung zur Meinung des Hauptgesellschafters durch das Gremium. Der Beschluss ist gefasst!

Das Gesellschafterversammlung veröffentlicht direkt danach in der eigenen Firmenzeitung unter anderen Beschlüssen den folgenden bemerkenswerten Eintrag:

Beschluss Nr V21/2014 des Geschäftsleitung vom 14.1.2014: Weil es schlecht für das Betriebsklima ist, ist dieses Treiben mit sofortiger Wirkung untersagt.

Die verdutzten Mitarbeiter kratzen sich bei der Lektüre dieser Meldung am Kopf und erklären hinter vorgehaltener Hand die Geschäftsleitung aufgrund irreversibler Verblödung für handlungsunfähig.

1.1 Was ist da falsch gelaufen?

Im Grunde genommen hatten alle Beteiligten ja nur das Beste im Sinne und keiner hat sich wirklich etwas vorzuwerfen.

Aber: Die Gesellschafterversammlung hat sich des Instruments des Beschlusses bedient, um ein Betriebsproblem aus der Welt zu schaffen. Von Beschlüssen erwarten wir vor Allem, dass sie anwendbar sind. Und genau hier ist das Problem: Das, was innerhalb der Gesellschafterversammlung noch vollkommen klar war, nämlich dass sich der Programmierer Krause jeden Mittag aus der nahegelegenen Metzgerei ein Frikadellenbrötchen holt und es deswegen im Großraumbüro so fürchtbar riecht, dass sich keiner mehr konzentrieren könnte und das das für Unmut unter den Kollegen geführt hat, genau das ist ausserhalb der Besprechung nicht klar.

Der Fehler ist aber weder ausserhalb noch innerhalb der Besprechung gemacht worden sondern exakt in dem Moment, in dem Protokollführer den fertiggestelleten Beschluß am Ende der Besprechung aus dieser heraustransportiert hat, um ihn in die Firma zu entlassen. In genau diesem Moment und nicht früher oder später hätte er den aktuellen Kontext, unter dem der Beschluss gefasst wurde, festhalten und dem Beschluss hinzufügen müssen:

Er muss also ein Paket bilden aus

  1. Dem Kontext
    • TREIBEN = Frikadellen im Büro essen
    • ES = der Gestank im Büro
  2. Der Erklärung
    • Weil ES schlecht für das Betriebsklima ist, ist dieses TREIBEN mit sofortiger Wirkung untersagt.

2 …und genau dieses Paket ist eine Closure

Denn überall wo diese Erklärung zitiert, gespeichert oder gedruckt wird, kann dann die vollständige Bedeutung entnommen werden.

Sie lautet:

Weil der Gestank im Büro schlecht für das Betriebsklima ist, ist dieses Frikadellen im Büro essen mit sofortiger Wirkung untersagt

3 In Lisp

Ich werde jetzt den ganzen schiefgelaufenen Vorgang in Lisp formulieren und Lisp wird ohne Zutun für alles sorgen und den Fehler des Protokollführers nicht wiederholen.

Zunächst legen wir die Tagesordnung für die Geschäftsleitung am 14.1 fest. Sie ist eine Funktion, die erstens mit LET den Kontext der Besprechung festlegt und zweitens unter diesem Kontext eine andere Funktion – nämlich den Beschluß – zusammenbaut, die diesen Kontext verwendet. Diese zweite, zurückgegebene Funktion beginnt mit LAMBDA und sie druckt einfach einen Text aus, der den Kontext der äusseren Funktion, nämlich der Geschäftsführerversammlung verwendet.

(defun GF-Versammlung-am-14-1 ()
  (let ((treiben '(Frikadellen im Büro essen))
        (es '(der gestank im Büro))
        (meier '(ist ein blöder schwätzer)))
    (lambda (stream)
      (print (list 'weil es 'schlecht 'für 'das 'betriebsklima 
                   'ist '/ 'ist 'dieses treiben 'mit 
                   'sofortiger 'wirkung 'untersagt)))))

Lisp antwortet auf diese Definition gewissermassen mit der Einladung alle Mitglieder der Geschäftsführung:

GF-VERSAMMLUNG-AM-14-1

Der große Tag, der mit Spannung erwartet wurde, sei jetzt gekommen und wir rufen die GF-Versammlung auf. Innerhalb dieser werden, wie gesagt, zunächst mit LET die Kontextvariablen TREIBEN und ES inititialisiert und schließlich der Beschluss als anonyme Funktion gefasst und zurückgegben. Wir speichern ihn daher beim Aufruf der Besprechung direkt in der Variablen *beschluss-über-gestank* :

(defparameter *beschluss-über-gestank* (gf-versammlung-am-14-1))
*BESCHLUSS-ÜBER-GESTANK*

Nun ist die GF-Versammlung also beendet (hocheffizient, dauerte nur Millisekunden!) und übrig bleibt nichts weiter als ihre Ausgabe, der Beschluss.

Zu beachten ist, dass die Bindung der inneren Variablen TREIBEN und ES zwar in der Versammlung, aber nicht innerhalb des Beschlusses erfolgt ist. Zum jetzigen Zeitpunkt ist die Versammlung ja auch bereits gelaufen und dieser Kontext existiert auch gar nicht mehr. Aber, in dem Moment, in dem der Beschluss die Funktion "Versammlung" verlassen hat, hat Lisp für uns den Kontext zu diesem hinzu gepackt und uns dieses Paket, die Closure eben, zurück gegeben. Genau das, was der Protokollführer oben versäumt hat! Den zusätzlichen Kontext in der Versammlung MEIER = (IST EIN BLÖDER SCHWÄTZER) (der einzige Wortbeitrag von Schulze in dieser Besprechung), hat Lisp geflissentlich nicht mit in den Beschluss eingebaut, weil der Beschluss selbst nicht auf Meier bezug nimmt. Genau so eben, wie ein anständiger Protokollführer!

Wir schauen uns dieses Ergebnis einmal an:

*beschluss-über-gestank*
#<FUNCTION (LAMBDA (STREAM) :IN GF-VERSAMMLUNG-AM-14-1) {1005FBC12B}>

Der Beschluss ist also eine Funktion (unsere Closure!) und noch kein Text!

Die Firma kann jetzt durch Aufruf der Funktion *BESCHLUSS-ÜBER-GESTANK* direkt mit der völlig korrekten Veröffentlichung in ihrer Firmenzeitung mit dem schönen Namen "*STANDARD-OUTPUT*" loslegen:

(funcall *beschluss-über-gestank* *standard-output*)

Und es landet dadurch folgender Text in *standard-output* :

:(WEIL (DER GESTANK IM BÜRO) SCHLECHT FÜR DAS BETRIEBSKLIMA IST / 
IST DIESES (FRIKADELLEN IM BÜRO ESSEN) MIT SOFORTIGER WIRKUNG UNTERSAGT)

Ich gebe zu daß dieses Beispiel etwas sonderbar ist, aber ich wollte der endlosen vielen Closure-Einführungen in Tutorials, in denen gewöhnlich eine ominöse, ADDER genannte Funktion vorkommt, nicht noch eine Weitere hinzufügen.

In meinem nächsten Artikel, den ich gerade vorbereite, und der eine sehr praktische DSL (Domain-Specific-Language) vorstellen wird, kommen so viele Closures vor, dass Sie spätestens nach dessen Durcharbeit nie wieder fragen werden, wofür Closures da sind.

Author: Patrick Krusenotto (patrick.krusenotto@googlemail.com)

Date: 2014-10-08 22:56:13 CEST

Generated by Org version 7.8.11 with Emacs version 24

comments powered by Disqus