Ist deklarativ wirklich instruktiv?

Werbung
Februar 2015
IST DEKLARATIV
WIRKLICH
INSTRUKTIV?
Eine kritische Betrachtung
Dr. Jürgen Lampe
Agon Solutions
IST DEKLARATIV WIRKLICH INSTRUKTIV
1
Abstract
Code wird vor allem gelesen. Deshalb ist es wichtig, dass Programmiersprachen intuitiv zu
verwenden sind und Beschreibungen unterstützen, die beim Lesen instruktiv empfunden
werden.
Wenn es darum geht, die Vorteile von Sprachelementen wie Lambdas oder StreamProcessing herauszustellen, wird sehr oft darauf verwiesen, dass sie einen deklarativ
geprägten Programmierstil erlauben. Deklarative Programmierung wird dabei per se als
erstrebenswert und vorteilhaft bewertet. Ist diese Annahme wirklich richtig, und was ist
deklarative Programmierung überhaupt?
Im Folgenden wird zunächst eine praktikable Abgrenzung des Begriffs versucht. Darauf
aufbauend werden einige grundlegende Probleme genauer erörtert und danach die
Rahmenbedingungen geeigneter Anwendungsszenarien beschrieben.
Was ist deklarative Programmierung?
Der Begriff Deklarative Programmierung ist nicht neu. Das Thema ist inzwischen über 40
Jahre alt, hat seine suggestive Kraft aber offensichtlich nicht verloren. Es scheint, dass
neben Microsofts Sprache F# insbesondere die Diskussionen um die Spracherweiterungen
für Java 8 ihm neues Leben eingehaucht haben. Nicht alle sind darüber glücklich, recht
polemisch schreibt zum Beispiel Robert Haper in einem Blog-Beitrag „..I was surprised by
the declarative zombie once again coming to eat our brains.” [1] Um die Berechtigung dieser
Bemerkung bewerten zu können, ist es erforderlich, zunächst ein tieferes Verständnis zu
gewinnen.
Wie so oft bei Schlagworten zeigt sich auch hier, dass eine klare und allgemein anerkannte
Begriffsbildung fehlt. Meist werden logische und funktionale Programmierung dazu
gerechnet, manchmal auch mathematische Modelle und domänenspezifische Fachsprachen.
Gemeinsam ist allen Definitionsansätzen, dass sie als wichtiges Kennzeichen die
Beschreibung des Was anstelle des Wie hervorheben. Während ein imperatives oder
prozedurales Programm den Weg beschreibt, auf dem durch schrittweise
Zustandsänderungen das gewünschte Ziel (=Ergebnis) erreicht werden kann, ist ein
deklaratives Programm die genaue Beschreibung (Spezifikation) eben dieses Ergebnisses.
Es gibt keine Manipulationen eines zeitlich veränderbaren Zustands. Die deklarative Sicht ist
daher eine vorwiegend statische und entspricht am ehesten der Verwendung von
Blaupausen als Produktvorlage.
Wenn auf die Spezifizierung der Ausführung verzichtet wird, öffnet das die Freiheit,
unterschiedliche Wege nutzen zu können. Besonders betont wird gern die Chance für
Optimierungen, die unabhängig von der gerade behandelten Aufgabe sind. Im Idealfall kann
der deklarative Code später einmal durch ein Verfahren ausgeführt werden, dass zum
Zeitpunkt des Schreibens noch gar nicht bekannt war. Ein oft zitiertes Beispiel dafür ist die
implizite Parallelisierung, die allerdings wieder ganz andere Fragen aufwirft und deshalb hier
IST DEKLARATIV WIRKLICH INSTRUKTIV
2
nicht weiter betrachtet werden soll. Um auf den erwähnten Idealfall zurückzukommen, der
begegnet einem auch in der Programmierung so selten wie im normalen Leben.
Das Verführerische an deklarativen Beschreibungen liegt darin, dass sich unzählige sofort
einleuchtende Beispiele konstruieren lassen, deren Kompaktheit und Klarheit überzeugend
wirkt. Bei solchen kleinen Beispielen ist der Effekt, der sich durch den Entfall relativ
konstanter Codemuster (sogenannter „Boilerplate-Code“) besonders beeindruckend. Was
dabei leicht übersehen wird, ist, dass dieser Effekt nicht skaliert. Je größer und vielfältiger
ein Problem ist, desto geringer wird der relative Anteil der konstanten Fragmente.
Gleichzeitig steigen die Ansprüche an die deklarative Sprache bezüglich der
Ausdrucksstärke, um solche komplizierteren Zusammenhänge darstellen zu können. Nur
wenn dann noch eine echte Vereinfachung zu sehen ist, lohnt es, ernsthaft über deklarative
Programmierung nachzudenken. Der reine Wegfall von Standard-Rahmencode allein
rechtfertigt es jedenfalls nicht.
Für die weitere Diskussion soll als Beispiel für eine eher deklarativ geprägte Sprache SQL
benutzt werden. Ein anderer prominenter Kandidat für diese Rolle wäre HTML. Ebenso sind
Java-Annotationen deklarative Ausdrucksmittel. Als typischer Vertreter prozeduraler
Sprachen dient der Kern von Java.
Das Beispiel 1 stellt die beiden Paradigmen gegenüber. Während das (deklarative) SQLStatement nur die Ergebnismenge definiert, beschreibt der Java-Code genau, wie das
Ergebnis erzeugt werden soll. (Korrekterweise gehörte auch der Code der verwendeten
toString-Methode noch dazu.)
Java:
for (datensatz: datensaetze) {
if (datensatz.id == 0)
System.out.println(datensatz.toString());
}
SQL:
SELECT * FROM datensaetze WHERE id=0
Beispiel 1: Vergleich prozedurale und deklarative Programmierung
Es ist deutlich zu erkennen, dass in diesem Fall der SQL-Code wesentlich kürzer ist und eine
nicht geordnete Liste aller Datensätze, deren id gleich null ist, übersichtlicher beschreibt.
Allerdings erhält man im Gegensatz zur Java-Lösung keinerlei Information über die
physische Reihenfolge, die in der Liste eventuell die Folge der Einfügungen ist.
Operationalisierung
Die Tatsache, dass deklarative Programme das Was, aber nicht das Wie einer
Problemlösung beschreiben, macht es notwendig, dieses Wie automatisch zu ermitteln. Die
IST DEKLARATIV WIRKLICH INSTRUKTIV
3
Implementierung einer deklarativen Programmiersprache erfordert daher zwingend die
Existenz eines Algorithmus für das Finden und Zuordnen eines Ausführungsverfahrens.
Diese Zuordnung wird Operationalisierung genannt. Sie setzt voraus, dass jedem korrekten
Ausdruck in der deklarativen Programmiersprache eine eindeutige Bedeutung zugeordnet
werden kann. Die Definition der Bedeutung erfolgt dabei durch ein formales Modell des
Objekt- oder Domänenbereichs.
In diesem Abschnitt werden einige wichtige Fragen, die bei der Implementierung deklarativer
Programmiersprachen auftreten, diskutiert.
Existenz eines effektiven Verfahrens
Dies ist die Grundbedingung. Es muss mindestens ein Verfahren existiert, welches unter
allen denkbaren Bedingungen einen zulässigen Ausgangszustand in den gewünschten
(deklarierten) Endzustand überführt, d. h. effektiv ist. Das setzt ein gutes Verständnis und die
Modellierbarkeit der zugrunde liegenden Domäne voraus. Für das Beispiel SQL ist diese
theoretische Basis durch das relationale Datenmodell gegeben. Typischerweise handelt es
sich um einen klar abgegrenzten, beschränkten Objektbereich.
In semantisch reicheren Modellen zeigt sich leider oft, dass wichtige Fragen prinzipiell
unentscheidbar sind. Dann bleibt zum einen der Rückgriff auf heuristische Verfahren mit dem
Risiko einer unvollständigen Operationalisierung. Ein anderer Weg mit dieser Situation fertig
zu werden ist der, die formale Sprache zusätzlichen Restriktionen zu unterwerfen. Ein
typisches Beispiel für diesen Weg liefern Parser für kontextfreie Sprachen. Sie werden
üblicherweise durch die Regeln einer Grammatik beschrieben – ein klassischer Fall von
deklarativer Programmierung. Die Form der zulässigen Regeln wird jedoch fast immer durch
den verwendeten Analyse-Algorithmus eingeschränkt. Genau genommen widersprechen
solche Restriktionen dem deklarativen Charakter der Beschreibung. Aber während sich für
Parser die meisten dieser zusätzlichen Bedingungen auf der Ebene des Formalismus (als z.
B. rechts- bzw. linksrekursive, LL(n)- oder LR(n)-Grammatik) beschreiben und statisch
überprüfen lassen, ist das in anderen Anwendungsbereichen seltener der Fall.
Effizienz des Verfahrens
Wenn ein effektives Verfahren existiert, d. h. die Lösung prinzipiell möglich ist, stellt sich als
nächstes die Frage nach der Effizienz dieses Verfahrens. Trotz der rasanten Entwicklung der
Computertechnik wird die Verarbeitungsleistung immer ein Thema bleiben, weil dadurch u. a.
Grenzen der Anwendbarkeit bestimmt werden. Grundsätzlich wird ein Verfahren umso
effizienter gestaltet werden können, je besser es an die ganz konkrete Aufgabe angepasst
wird. Es ist ein prinzipielles Problem der deklarativen Programmierung, dass bei der
Operationalisierung besondere Eigenschaften des konkreten Anwendungsfalls nur begrenzt,
nämlich nur soweit sie sich in der Beschreibung manifestieren, berücksichtigt werden
können.
Aufgaben mit einem begrenzten Lösungsraum können immer durch systematisches
Durchprobieren aller Möglichkeiten (Brute-Force- oder Exhaustionsmethode) gelöst werden.
Dieser Ansatz ist jedoch wenig effizient und schränkt die mögliche Größe der behandelbaren
Probleme stark ein. Doch auch bei ausgefeilteren Verfahren kann es vorkommen, dass die
IST DEKLARATIV WIRKLICH INSTRUKTIV
4
allgemeine Effizienz nicht den Anforderungen der Praxis genügt. Wenn kein besserer
Algorithmus bekannt ist, gibt es unterschiedliche Wege, trotzdem akzeptable
Implementierungen zu erreichen:
• Der deklarative Formalismus wird um operative Elemente erweitert, die helfen, den
Suchraum einzuschränken. Um sie richtig zu verwenden sind gute Kenntnisse des
(eigentlich verborgenen) Verfahrens unerlässlich. In diese Kategorie fallen die Indizes in
SQL. Sie können – richtig angewandt - die Laufzeit erheblich beeinflussen, haben aber
auch das Potential, aus einem (deklarativ) korrekten Statement eines zu machen, das
(operativ) einen Deadlock erzeugt.
• Für die Implementierung wird ein „besserer“ Algorithmus ausgewählt, der jedoch weitere
Einschränkungen für den deklarativen Formalismus erfordert. Das muss keine schlechte
Lösung sein. In vielen Fällen ist es allerdings schwierig, diese Einschränkungen in den
Kategorien des Formalismus zu definieren.1 Muss man aber das unterliegende
Ausführungsmodell zu genau kennen, entfällt ein wichtiger Vorteil des deklarativen
Paradigmas.
Wenn auf andere Weise die erforderliche Effizienz nicht erreicht werden kann, endet
allerdings der praktische Einsatz eines deklarativen Programmiermodells viel zu oft in einer
schwer zu beherrschenden Mischform mit zahlreichen prozeduralen Erweiterungen.
Noch deutlicher als bei den Ergänzungen, die SQL zu einer mehr oder weniger „normalen“
Programmiersprache machen sollen, zeigt sich diese Konsequenz, wenn HTML durch
JavaScript erweitert wird.
Implizite Abhängigkeiten
Deklarative Programme sollen effiziente Implementierungen dadurch unterstützen, dass sie
bewusst Freiheiten lassen. Festlegungen der Art „In diesem Fall ist das Ergebnis
undefiniert.“ haben sich in der Praxis allerdings nicht bewährt. Das ist leicht erklärbar: Die
Richtigkeit eines formalen Ausdrucks wird häufig nicht durch eine abstrakte Prüfung, sondern
durch einen Test mit einer konkreten Implementierung ermittelt. Ob dabei das undefinierte
Verhalten adäquat berücksichtigt wird, ist praktisch nicht verifizierbar. Das Gefährliche an
solchen verdeckten Implementationsabhängigkeiten ist, dass sie schwer oder gar nicht zu
erkennen sind und dadurch kaum vermieden werden können.
Neben solchen funktionalen Abhängigkeiten können auch nichtfunktionale auftreten, zum
Beispiel in Bezug auf Laufzeit oder Speicherplatzbedarf.
Dieser Punkt ist keine akademische Spitzfindigkeit, wie jeder bestätigen wird, der schon
einmal SQL-Skripte von einem auf ein anderes DBMS migriert hat.
11
Ein Beispiel dafür ist die bei naiver Implementierung von Parsern mittels rekursiven Abstiegs anzutreffende
Bedingung, dass Alternativen so geordnet sein müssen, dass bei gemeinsamen Präfixen, der längere immer vor
dem kürzeren stehen muss, also nicht (int|integer), sondern umgekehrt, was bei Beteiligung von
Nichtterminalen schwierig zu erkennen ist.
IST DEKLARATIV WIRKLICH INSTRUKTIV
5
Das Basismodell
Das Modell, welches wie bereits erwähnt, die Bedeutung der deklarativen formalen Sprache
definiert, ist der Dreh- und Angelpunkt. Es entscheidet sowohl über die
Operationalisierbarkeit als auch die dauerhafte Anwendbarkeit in der Praxis.
Ein eindrucksvolles Beispiel für die Rolle des Modells ist die Entwicklung von HTML.
Entworfen als logische Auszeichnungssprache für Web-Dokumente ist sie ohne Zweifel
deklarativ. Was jedoch von Anfang an gefehlt hat, ist ein klar definierter Begriff dessen, was
ein Web-Dokument im Detail ist und welche Struktur es haben kann. Das ist ein
grundsätzlicher Unterschied zu beispielsweise Donald Knuths Satzsystem TeX. Die Folgen
sind bekannt und heute noch spürbar. Über Jahre haben inkompatible Browser allen
Beteiligten das Leben schwer gemacht. Es hat lange gedauert, bis einigermaßen klar war,
was überhaupt zulässiger HTML-Text ist. Einen wirklichen Fortschritt hat erst das Document
Object Model (DOM) des W3C gebracht, indem es ein verbindliches Strukturmodell definiert,
auf das sich die Auszeichnungen beziehen. (Und das mit Scriptsprachen manipuliert werden
kann.)
Das semantische Modell
Deklarativen Ausdrücken liegt eine im Vergleich zu prozeduralen Ausdrücken abstraktere
Semantik zugrunde. Das ermöglicht auf der einen Seite kompaktere Beschreibungen,
erfordert auf der anderen jedoch, dass dieses semantische Modell von allen, die den Code
schreiben, modifizieren oder lesen, gleichermaßen verstanden wird.
Das ist eine wesentliche Restriktion des deklarativen Paradigmas: Es setzt voraus, dass ein
hinreichend bekanntes Basismodell existiert. In der Informatik trifft das für viele einfache
Strukturen zu, aber es fällt schwer, Beispiele zu finden, die mit der Komplexität des
relationalen Datenmodells vergleichbar sind.
Wichtig ist, dass die Konzepte des semantischen Modells möglichst orthogonal sind, wie das
im Fall von SQL für Relationen und Datentypen gilt. Denn da es sich beim semantischen
Modell um eine letztlich frei gewählte Abstraktion handelt, ist auch die Definition der
Interaktionen der beteiligten Konzepte frei wählbar. Die Erfahrung zeigt, dass abhängig vom
jeweiligen Modellierungsziel, solche Interaktionen verschieden interpretiert werden können
und auch werden. Semantische Modelle mit Varianten, besonders wenn sich diese nur auf
Details beziehen, sind jedoch eine denkbar schlechte Basis für die Verständigung.
Schließlich ist Programmtext immer auch Kommunikationsmedium zwischen Menschen.
Diese Rolle kann eine deklarative Sprache nur erfüllen, wenn ihr semantisches Modell
eindeutig verstanden wird und klar abgegrenzt ist.
Aus diesen Überlegungen folgt weiter, dass das semantische Modell nicht zu komplex sein
darf, weil die (lesende) Interpretation der deklarativen Beschreibung erfordert, alle jeweils für
das Verständnis relevanten Konzepte parat zu haben.
IST DEKLARATIV WIRKLICH INSTRUKTIV
6
Modellbeschränkungen
Ein Modell repräsentiert stets nur einen Teilaspekt der Wirklichkeit, der durch die Mengen
der modellierten Objekte und Operationen beschränkt wird. Leider halten sich die Wünsche
der Anwender nicht an die Grenzen der Modelle. Ein kleines Beispiel aus der täglichen
Praxis soll die Problematik illustrieren: Gefordert sei ein SQL-Select mit fortlaufender
Nummerierung der Ergebnisdatensätze. Diese scheinbar triviale Funktion ist nicht einfach zu
realisieren. Das liegt nicht an der Unzulänglichkeit von SQL, sondern daran, dass das
Konzept Nummerierung kein Bestandteil der Relationentheorie ist. Wenn die Ergebnismenge
vor der Nummerierung nicht sortiert wird, ist die Menge der Datensätze einschließlich
Zeilennummer nicht mehr unabhängig von Implementationsdetails, wie z. B. der physischen
Anordnung. D. h., eine einfache Nummerierung, wie sie bei prozeduraler Programmierung
mittels einer Indexvariablen realisiert würde, führt aus dem Definitionsbereich des Modells
hinaus.
Wie eine modellkonforme Nummerierung aussehen könnte, zeigt das Beispiel 2 (entnommen
aus [2]). Man sieht deutlich, dass an den Grenzen des Modells die Vorteile der deklarativen
Beschreibung verschwinden. Probleme dieser Art werden oft durch Ergänzung eingebauter
Funktionen wie hier beispielsweise ROWCOUNT oder ROW_COUNT zu entschärfen versucht.
SELECT rank=count(*), a1.au_lname, a1.au_fname
FROM authors a1, authors a2
WHERE a1.au_lname + a1.au_fname >= a2.au_lname + a2.au_fname
GROUP BY a1.au_lname, a1.au_fname
ORDER BY rank
Beispiel 2: MS SQL-Server – Ausgabe einer alphabetisch geordneten Liste von
Autorennamen und -vornamen mit vorangestellter laufender Nummer
Handhabbarkeit
Neben den erwähnten eher technischen und konzeptionellen Fragen wirft die deklarative
Programmierung in ihrem Gebrauch durch den Menschen Probleme auf.
Menschen denken nur sehr eingeschränkt deklarativ
Die meisten Menschen sind in ihrem Denken stark handlungsorientiert, insbesondere wenn
es um das Erreichen eines Ziels geht. Deklarative Beschreibungen werden, wie die schon
erwähnten Blaupausen, vorrangig für die Darstellung statischer Objekte eingesetzt. Nur in
Fällen, wo eine standardisierte Darstellungsform existiert und genügend Erfahrungen
bezüglich der Umsetzung vorliegen, reicht eine solche Beschreibung als Handlungsanweisung aus.
Wie schwer es Menschen fällt, deklarativ zu denken, kann man leicht am Beispiel von
Zeitungsanzeigen demonstrieren. Selbst bei lauterstem Bemühen klafft zwischen implizit
Vorgestelltem und explizit Spezifiziertem oft eine erhebliche Lücke.
IST DEKLARATIV WIRKLICH INSTRUKTIV
7
Deklarative Programmierung beschreibt Zustandsveränderungen in einem Schritt, ohne
Berücksichtigung interner Zwischenzustände und abstrahiert damit in gewisser Weise von
der Zeit. Leben ist aber durch Zeit geprägt. Es besteht aus einer fortlaufenden Folge von
Operationen, die den globalen Zustand verändern: Leben ist grundsätzlich prozedural. Der
Verzicht auf die zeitliche Komponente in verschiedenen Modellierungen ist eine erhebliche
intellektuelle Leistung. Ein Blick in die Wissenschaftsgeschichte zeigt, dass der Weg der
Herausbildung solcher Abstraktionen nicht einfach und konfliktfrei verläuft. Es ist eine Illusion
zu glauben, dass gute von der Zeit abstrahierende Modellvorstellungen im erforderlichen
Umfang ad hoc während der Programmerstellung entwickelt werden können.
Notation
Nicht in allen Fällen ist ein deklarativer Programmtext kompakter und übersichtlicher als ein
prozeduraler. Das gilt selbst, wenn man vollständig innerhalb der Grenzen der deklarativen
Sprache bleibt, wie das folgende SQL-Beispiel (Beispiel 3) veranschaulicht. Die Aufgabe
besteht darin, eine Liste von Dateinamen so auszugeben, dass eine Endung „txt“ durch
einen Punkt abgetrennt wird, also z. B. „alfatxt“ als „alfa.txt“. Der Code ist vereinfacht und
berücksichtigt nicht, ob der Punkt schon vorhanden ist. Trotzdem enthält er bereits fünfmal
den Teilausdruck length(datei) und zweimal length(datei)-2. Das Problem
wiederkehrender Teilausdrücke ist aus der funktionalen Programmierung bekannt. Den
Auswirkungen auf die Laufzeit wird mit Optimierungen (Common subexpression elimination)
versucht zu begegnen. Das hilft jedoch nicht gegen den offensichtlichen Mangel, dass der zu
schreibende Code unübersichtlicher und länger ist, als wenn man den Teilausdruck einer
Programmvariablen zuweisen und diese dann stattdessen verwenden würde.
SELECT if(length(datei)>3
AND (substr(datei, length(datei)-2, 3)='txt'),
concat(substr(datei, 1, length(datei)-3), '.',
substr(datei, length(datei)-2, 3)), datei)
FROM dateien WHERE length(datei)>0;
Beispiel 3: Wiederholung identischer Teilausdrücke
Ein weiterer Nachteil deklarativer Sprachen, der sich zwangsläufig aus ihrer Natur ergibt, ist
die Unmöglichkeit Interaktionen, beispielsweise Ein- und Ausgaben, angemessen zu
beschreiben. In der Regel werden diese Aufgaben als Seiteneffekte realisiert und sind als
solche in der Beschreibung eher verborgen als klar erkennbar.
Beeinflussbarkeit der Ausführung
Es ist gerade der größere Abstand zwischen Programmcode und Ausführung, der die
anwendungsorientierte Beschreibung erleichtert. Kein Vorteil ohne Preis: Durch diesen
größeren Abstand kann der durch die Maschine ausgeführte Code weniger beeinflusst
werden. In der Theorie ist das ein Vorteil. Auftretende Performanceprobleme sind durch
andere oder bessere Operationalisierungen zu beheben und keine Aufgabe für
IST DEKLARATIV WIRKLICH INSTRUKTIV
8
Anwendungsentwickler. In der Praxis muss man oft mit dem gerade Vorhandenen
auskommen. Das heißt nicht selten, statt der kurzen und klaren Formulierung auf eine solche
mit trickreichen Umwegen auszuweichen, um ein gestecktes Performanceziel doch noch zu
erreichen. Die möglichen Vorteile deklarativen Programmierens werden dadurch gleich in
zweierlei Hinsicht konterkariert: (1) Dadurch, dass die Sprachmittel, die zur Beschreibung
des Was entworfen wurden, zur Beschreibung des Wie missbraucht werden, wird die
Lesbarkeit zerstört. (2) Es wird genau das erreicht, was vermieden werden sollte: Die
Beschreibung wird von einer speziellen Implementierung abhängig.
Fehlersuche, Debugging, Test
Der große Abstand zwischen Beschreibung und Ausführung erschwert die Analyse von
Fehlern oder anderen unerwarteten Effekten. Spezielle Tools müssen gebaut und gewartet
werden. Außerdem steigt die Wahrscheinlichkeit, dass die Implementierung der beteiligten
Software selbst fehlerhaft ist, weil diese relativ komplizierter ist und wegen der höheren
Spezialisierung weniger häufiger verwendet wird.
Die Fehlersuche setzt ebenso wie die Suche nach Performancekillern oft eine intensive
Auseinandersetzung mit der jeweiligen Operationalisierung voraus – also genau das, was
der Gebrauch der deklarativen Sprache ersparen sollte. Nur wenn die Wahrscheinlichkeit,
dass derartige Probleme auftreten, hinreichend gering ist, rentiert sich die Verwendung einer
deklarativen Sprache.
Unser Beispiel SQL deckt einen breiten Anwendungsbereich ab, in dem solche speziellen
Kenntnisse über die Ausführung nicht benötigt werden. Wenn die Anforderungen hinsichtlich
Größe oder Performance ein bestimmtes Maß übersteigen oder bei bestimmten Fehlern,
sind jedoch Spezialisten gefordert, die das jeweilige DBMS sehr genau kennen. In der Regel
brauchen sie sich aber nur um einen kleinen Teil des Codes oder des Datenmodells zu
kümmern, sodass der größte Teil der SQL-Entwickler mit diesen Problemen nicht konfrontiert
ist. Wenn das anders wäre, hätte SQL wohl nie die Verbreitung gefunden, die es heute hat.
Modellgrenzen
Im Zusammenhang mit dem Basismodell wurde bereits auf dessen Grenzen hingewiesen.
Wenn das Modell die jeweilige Anwendungsdomäne nur teilweise überdeckt, wird man früher
oder später an dessen Grenzen stoßen. Denn Software lebt. Erfolgreiche Anwendungen
wachsen und werden erweitert. Beim Erreichen der Grenzen stellt sich die Frage: Wie
weiter? Die logisch konsequente Antwort müsste sehr oft lauten: komplette Neuentwicklung
auf allgemeinerer Basis. Praktisch ist das nur selten umsetzbar. Als Ausweg wird deshalb
häufig versucht, die deklarative Sprache so zu erweitern, dass sie den zusätzlichen
Anforderungen entspricht. Diese Erweiterungen werden sehr wahrscheinlich den
deklarativen Charakter der Beschreibung zerstören. Es stellt sich die Frage, ob eine solche
hybride Sprache überhaupt noch Vorteile bietet.
Am Beispiel SQL lässt sich das gut zeigen. Für Anwendungsfälle, die sich nicht mehr im
Rahmen des Relationenmodells beschreiben lassen, gibt es prozedurale Erweiterungen (PL
– Procedural Language). Sie sind für fast jede Datenbank verfügbar. Die damit verfassten
Skripte stehen allerdings nicht in dem Ruf gut wartbar zu sein, da schon bei einfachen
IST DEKLARATIV WIRKLICH INSTRUKTIV
9
Dingen Fallen lauern. (Vgl. z. B. für PL/SQL: „Types in PL/SQL can be tricky. … variable
assignments and comparisons may not work the way you expect.” [3])
Ein anderes Beispiel für den Umgang mit Modellgrenzen bieten die Build-Werkzeuge Ant
und Maven. Beide verfügen über einen Satz vordefinierter Funktionen, mit deren Hilfe ein
Build-Verlauf deklarativ beschrieben werden kann. Das ist sehr praktisch, so lange man sich
im Rahmen dessen bewegt, was vorgesehen ist. Davon abweichende Logik ist schwieriger
zu beschreiben: „All too often, when adding on to a build script, you can't shake the feeling
that you're implementing a workaround or hack.“ [4] Gar nicht so selten müssen deshalb
zusätzliche Funktionsbausteine (Module, Tasks) implementiert werden, um die Liste der
Grundfunktionen zu ergänzen. Abgesehen davon, dass solche Erweiterungen als
„Nebenprodukt“ nicht immer alle Qualitätsstandards erfüllen, erhält man in der Konsequenz
eine Menge (leicht) unterschiedlicher Modelle. Das ist eine für das Verständnis und die
Wartbarkeit der deklarativen Build-Beschreibungen ebenfalls ungünstige Situation.
Die verbleibende Unbestimmtheit
Ein deklaratives Programm beschreibt die unbedingt notwendigen Eigenschaften der
gewünschten Implementierung. Im Allgemeinen ist es nicht möglich, die Vollständigkeit einer
Spezifikation zu beweisen. Unterschiedliche Sichten auf das Anwendungsgebiet können zu
unterschiedlichen Interpretationen dessen führen, was als vermeintlich unstrittig oder
selbstverständlich nicht explizit festgelegt wird. Andererseits kann der Versuch, jedes Detail
explizit erfassen zu wollen, zu einer unhandlichen Überspezifikation führen und damit die
Wahrscheinlichkeit, dass Fehler passieren, erhöhen.
An diesen Problemen ist schon vor mehr als 20 Jahren die automatische Verifikation von
Programmen gescheitert. Deren Ziel war es, zu zeigen, dass ein zu prüfender Code, d. h.
das Ergebnis der Operationalisierung, der Spezifikation, also der deklarativen Beschreibung
entspricht. Obwohl viele technische Probleme gelöst werden konnten, spielt dieses Thema
heute nur noch eine untergeordnete Rolle.
Die verbleibende Unbestimmtheit kann zu einer Bremse für die Weiterentwicklung werden.
Um eine bestehende Code-Basis nicht völlig zu entwerten, muss unter Umständen die
Kompatibilität auf die bereits erwähnten impliziten Abhängigkeiten ausgedehnt werden.
Radikale Wechsel der Implementierungsstrategie werden somit praktisch ausgeschlossen,
selbst wenn sie deutliche Verbesserungen bringen würden.
Spezielle Probleme in hybriden Sprachen
In der Praxis werden deklarative Ausdrucksmittel häufig als Teil einer prozeduralen
Programmiersprache eingesetzt. Die Integration derart unterschiedlicher Betrachtungsweisen ist nicht trivial. Am Beispiel Java/JavaEE zeigt sich, dass damit zwar die Fähigkeiten
des Werkzeugs Programmiersprache erweitert werden, aber gleichzeitig die Anforderungen
an Disziplin und Verantwortungsbewusstsein bei der Verwendung steigen.
IST DEKLARATIV WIRKLICH INSTRUKTIV
10
Verschiedene Idiome
Als einer der großen Vorzüge von Java gegenüber C++ galt immer die größere Homogenität
der Sprachmittel. Durch die deklarativen Erweiterungen wird dieser Vorsprung etwas kleiner.
Dabei muss die Möglichkeit, in unterschiedlichen Idiomen programmieren zu können, nicht
automatisch schlecht sein, Solange es gelingt, in Projekten oder Teilen davon, einen Stil
konsequent durchzuhalten, ist alles in Ordnung.
Die Lambda-Ausdrücke von Java 8 machen es in einigen Bereichen jedoch komplizierter.
Das soll am Beispiel der Listener gezeigt werden. Listener sind die in Java übliche
Implementation des Beobachter-Designmusters. Beispiel 4 zeigt den bisher üblichen Code
für das Hinzufügen zweier Listener (TreeExpansionListener und TreeSelectionListener), die
als anonyme Klassen geschrieben werden.
JTree tree= new JTree();
tree.addTreeExpansionListener(new TreeExpansionListener() {
@Override
public void treeExpanded(TreeExpansionEvent event) {
handleExpand();
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
handleCollapse();
}
});
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent event) {
handleSelect();
}
});
Beispiel 4: Listener als anonyme Klassen
Unter Verwendung von Lambdas lässt sich das kürzer schreiben, allerdings nur für den
TreeSelectionListener. Beispiel 5 zeigt die „lambdafied“ Version des Beispielcodes. Das sieht
auf den ersten Blick nur unschön aus. Tatsächlich handelt es sich um gänzlich verschiedene
Abstraktionen, die beim Betrachten einen Wechsel des „Lesemodus“ erfordern, also
zusätzlichen geistigen Aufwand verursachen. Code zu verstehen ist aber bereits schwierig
genug, da sollte jede Erschwerung, und sei sie noch so gering, vermieden werden.
Natürlich ist es möglich, den Code so zu gestalten, dass solche Wechsel zumindest
innerhalb einer Klasse nicht vorkommen. Die verbreiteten IDE bieten Refactoring-Funktionen
für beide Umwandlungen an, sodass auch, wenn beispielsweise der TreeExpansionListener
erst später dazu kommt, der Code in der Umgebung (Klasse) ohne großen Aufwand
angepasst werden kann.
IST DEKLARATIV WIRKLICH INSTRUKTIV
11
JTree tree= new JTree();
tree.addTreeExpansionListener(new TreeExpansionListener() {
@Override
public void treeExpanded(TreeExpansionEvent event) {
handleExpand();
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
handleCollapse();
}
});
// Listener in Lmabda-Notation:
tree.addTreeSelectionListener(event -> handleSelect());
Beispiel 5: „Lambdafied“ Listener
Was steigt sind allerdings die Anforderungen an die fachliche Projektleitung, weil die
Entscheidung für ein Idiom weitgehend Ermessenssache ist und die Angemessenheit nicht
durch Tools wie PMD automatisch überprüft werden kann. Nur durch regelmäßige und
qualifizierte Codereviews kann gesichert werden, dass die neuen Möglichkeiten nicht dazu
missbraucht werden schwerer lesbaren Code zu erzeugen.
Deklarativ objektorientiert?
Das ist zweifellos eine spannende Frage, aber sie kann hier nicht beantwortet werden. Bei
der Erweiterung von Java hat man sie auch nicht gestellt, sondern sich ganz pragmatisch auf
eine möglichst harmonische Integration der neuen Features in die Implementierung
konzentriert. Das ist bemerkenswert gut gelungen, was leider nicht in methodischer Hinsicht
gilt. Java 8 erlaubt da Fehler, die bisher nicht möglich waren.
Ein Ziel objektorientierter Programmierung besteht darin, den Einfluss, den lokale
Änderungen auf die Korrektheit des Gesamtprogramms haben können, zu minimieren. Eine
bisher gültige Regel lautet: Wird ein Interface erweitert, so sind davon nur Klassen betroffen,
die dieses Interface implementieren, aber nicht diejenigen, die es verwenden. Durch die
Einführung von Default-Methodenimplementierungen erlaubt Java 8 nun sogar eine
Erweiterung, ohne implementierende Klassen ändern zu müssen. Das geschieht allerdings
um den Preis, dass in Klassen, die das Interface verwenden, nachträglich Fehler auftreten
können.
Die Schuld an diesem kritikwürdigen Umstand trägt eine Entscheidung, die LambdaAusdrücke breiter anwendbar machen soll, nämlich die, alle Interfaces mit genau einer
Methode als funktionale Interfaces zu akzeptieren. Die möglichen Auswirkungen seien
anhand des im vorangehenden Abschnitt benutzten Beispiels erläutert.
Im (hypothetischen) Fall, dass das Interface TreeSelectionListener wie im Beispiel 6
dargestellt, erweitert wird, wäre der Code aus Beispiel 5 (im Unterschied zu dem aus
Beispiel 4) nicht mehr korrekt, weil die ursprünglich implizit vorhandene Eigenschaft
„funktionales Interface“, die die Voraussetzung für die Verwendung als Lambda-Ausdruck ist,
durch die neue, zweite Methode nicht mehr gegeben ist.
IST DEKLARATIV WIRKLICH INSTRUKTIV
12
public interface TreeSelectionListener extends EventListener {
void valueChanged(TreeSelectionEvent e);
/** Hypothetical new method */
default void valueChanged(TreeSelectionEvent e, int clickCount) {
// as default ignore
}
});
Beispiel 6: Erweitertes Interface
Das Problem ist leicht zu beheben, indem der Lambda-Ausdruck in eine innere Klasse
umgewandelt wird, trotzdem ist so etwas ärgerlich. Man kann, soweit es sich um Code im
eigenen Einflussbereich handelt, diesen Fehler vermeiden, indem man für LambdaAusdrücke nur Interfaces verwendet, die mit @FunctionalInterface annotiert sind. Auch
das ist wieder eine Frage von Disziplin und Kontrolle. (TreeSelectionListener ist laut API
übrigens nicht so annotiert.)
Annotationen
Die Annotationen haben im Laufe der Java-Entwicklung den größten Bedeutungswandel
erlebt. Ursprünglich als Hilfsmittel bei der Dokumentationsgenerierung erdacht, sind sie mit
der Zeit zu einem wichtigen Bestandteil der Sprache geworden.
Ohne Zweifel sind Annotationen deklarative Sprachelemente mit sehr einfacher Syntax und
ohne fest umrissene Abgrenzung möglicher Bedeutungen. Das Letztere ist besonders
kritisch zu sehen. Annotationen werden für ganz unterschiedliche Zwecke verwendet, z. B.:
• Dokumentation, Hinweise (@Deprecated, …)
• Fehlervermeidung, Dokumentation (@Override, …))
• Compilersteuerung (@SuppressWarnings, …))
• Definition neuer Annotationen (@Retention, …))
• Frameworkfunktionen (@Entity, @Inject, …)
• ..
Das alles in ein und derselben syntaktischen Form. Es macht den Eindruck, dass
Annotationen ganz allgemein als Mittel der dynamischen Spracherweiterung dienen müssen.
Damit sind sie aber überfordert. So bequem sich das beim Schreiben darstellen mag, so
unbequem ist es beim Lesen, weil sich der Inhalt nicht aus der Form erschließt und die
jeweilige Bedeutung an ganz unterschiedlichen Stellen definiert sein kann.
Ganz generell wird der Nutzen dynamisch erweiterbarer Sprachen in der
Softwareentwicklung kritisch gesehen. Die dadurch erreichbare Flexibilität macht die
Systeme insgesamt deutlich komplexer zu verwalten. Das gilt uneingeschränkt auch für die
leichtfertige Verwendung von Annotationen. In der aktuellen Version JavaEE 7 ist die Grenze
offensichtlich bereits überschritten. Man denkt unwillkürlich an Datenbankadministratoren,
die beim Anlegen einer neuen Tabelle vorsichtshalber immer gleich ein paar CustomXXSpalten ergänzen (, was ja nicht schlecht sein muss). Um Probleme schnell zu lösen, werden
in solche Spalten später dann u. U. schon mal Fremdschlüssel, gern auch auf
unterschiedliche Tabellen, eingetragen. So kann man zwar oft einfach und schnell eine
Lösung finden, baut sich längerfristig aber eine echte Herausforderung für die Wartung,
IST DEKLARATIV WIRKLICH INSTRUKTIV
13
insbesondere den Erhalt der Integrität, auf. Annotationen scheinen auf dem besten Weg,
innerhalb JavaEE die Rolle solcher magischen Joker zu übernehmen.
In Frameworks werden Annotationen u. a. für die folgenden zwei Aufgaben eingesetzt:
1. Als Kennzeichen, dass ein Objekt, Methode, o. Ä. in einer bestimmten Weise verwendet
werden kann oder soll, ohne große Annahmen über seine Umgebung zu machen, z. B.
@Entity, @WebService
2. Als Kennzeichen wie 1, aber unter der Voraussetzung, dass in der Ausführungsumgebung
(Container) bestimmte Bedingungen erfüllt sind, z. B. @Inject
Im ersten Fall handelt es sich gewissermaßen nur um ein Angebot, mit Hilfe von Tools
beispielsweise ergänzende Funktionalität zu generieren, d. h., die Annotationen haben aus
Sicht des Java-Programms informierenden Charakter. Das ist eine originär deklarative und
angemessene Funktion.
Der zweite Fall ist der kritischere. Die Gesamtheit aller derartigen Annotationen stellt eine
verteilte (teilweise) Spezifikation des globalen Zustands dar. Quasi beiläufig hat man sich ein
ernsthaftes Problem eingehandelt, zu dessen Beherrschung angemessene Werkzeuge
fehlen. Verteilte Spezifikationen werfen immer die Frage auf, wie die globale Konsistenz
gewährleistet werden kann. Bei Annotationen ist das im Allgemeinen zur Entwurfszeit nicht
möglich.
Beispiel 7 zeigt einen kleinen Ausschnitt aus einer Bean-Anwendung, die mittels Contextund Dependency-Injection (CDI) initialisiert werden soll. Das Feld service ist durch die
@Inject-Annotation als sogenannter Injection-Point deklariert worden. Service muss ein
Interface sein. Bei der Erzeugung von ServiceClient-Objekten wird durch die
Laufzeitumgebung (Container) ein entsprechendes, den Service implementierendes Objekt
gesucht und dem Feld zugewiesen. Diese Injection funktioniert aber nur dann in der
beschriebenen Form, wenn es genau eine Bean-Klasse gibt, die das Interface Service
implementiert. Da eine Anwendung aus mehreren Bean-Bibliotheken zusammengesetzt sein
kann, lässt sich diese Bedingung frühestens beim Deployment verifizieren. Im Klartext heißt
das: Allein durch das Zufügen einer Bean, die ein bestimmtes Interface (ein zweites Mal)
implementiert, kann die Lauffähigkeit einer Anwendung zerstört werden. Das steht im totalen
Widerspruch zur Intention von Interfaces, die ja genau diese Kopplung von Definition und
Implementierung aufbrechen sollen.
Es gibt zwar Möglichkeiten, die Einschränkung auf genau eine Implementierung zu
umgehen, aber am grundsätzlichen Problem ändert sich nichts, weil dabei nur, z. B. durch
ergänzende Qualifier-Annotationen, eine engere Eingrenzung erfolgt, die letztlich aber
ebenfalls wieder genau eine Klasse liefern muss.
public class ServiceClient {
@Inject
private Service service;
…
}
Beispiel 7: Context-Injection
IST DEKLARATIV WIRKLICH INSTRUKTIV
14
Man sollte Annotationen als das verwenden, was sie ihrem Namen nach sind: Anmerkungen,
ergänzende Hinweise, die das Verständnis des annotierten Programmtexts für den Leser,
den Compiler, die JVM oder andere Tools erleichtern. 2 Als Ersatz für fehlende Methoden
und Werkzeuge zum Modul- und Konfigurationsmanagement beispielsweise, sind sie fehl am
Platz.
Annotationen sind ein exzellentes Beispiel für den Fall, dass eine einfache Lösung für ein
einfaches Problem so erfolgreich ist, dass diese einfache Lösung danach so lange auf immer
größere Probleme angewandt wird, bis sie selbst zum Problem geworden ist. In dieser
Hinsicht
gibt
es
verblüffende
Parallelen
zur
Sprunganweisung
(Goto).
Aus gutem Grund hat Letztere aber nur in gezügelter (break, continue, return) bzw. impliziter
(if, for, …) Form in Java Eingang gefunden. (Für alle Fälle darf goto trotzdem nicht als
Bezeichner verwendet werden.)
Kurz zusammengefasst: Annotationen sind ein problematischer Bestandteil von Java. Das
liegt, abgesehen von der durch die primitive Form verursachten schlechten Erfassbarkeit,
allerdings weniger an ihrem deklarativen Charakter als an der ungezügelten Verwendbarkeit.
Schlussfolgerungen
Auch wenn bisher vorrangig die problematischen Seiten thematisiert wurden, ist deklarative
Programmierung nicht prinzipiell abzulehnen. Für den sinnvollen Einsatz müssen aber einige
Voraussetzungen erfüllt sein.
Eine geeignete Aufgabe
Wenn es um ein Verfahren selbst geht, das beschrieben werden soll, ist der deklarative Weg
ungeeignet. Prozedurale Programmiersprachen sind aus dem Bemühen heraus entstanden,
Algorithmen exakt formulieren zu können. Man brauchte diese Ergänzung der viel älteren
deklarativen mathematischen Formalismen einfach für die Steuerung von Computern.
Deklarative Programmierung abstrahiert u. a. von der Zeit. Reale Aufgaben sind jedoch
immer in einen zeitlichen Ablauf eingebunden. So werden SQL-Statements z. B. in einer
gewissen Reihenfolge ausgeführt. Die Zeit wird dabei gewissermaßen diskretisiert zu
Zeitpunkten vor und nach der Ausführung, der Zwischenzeitraum ignoriert. Daraus kann man
folgern, dass geeignete Aufgaben immer nur Teile der Gesamtaufgabe seien können, die in
ein umfassenderes prozedurales Programm eingebettet sind. (Im Beispiel ist dieses
prozedurale Programm einfach die Nacheinanderausführung, im Fall einer HTML-Seite die
Einbettung in den Request-Response-Zyklus.)
2
So wird es auch in der Java-Sprachspezifikation beschrieben: An annotation is a marker which associates
information with a program construct, but has no effect at run time. [6] Ob die Veränderung des auszuführenden
Codes beim Laden oder erstmaligen Ausführen ein Effekt auf die Laufzeit ist, mag im Detail kontrovers sein.
Klar ist, dass man damit zumindest in den Grenzbereich gerät.
IST DEKLARATIV WIRKLICH INSTRUKTIV
15
Hinreichende Bekanntheit des Basismodells
Nur wenn das semantische Modell ausreichend präzise definiert und in dieser Definition
bekannt und verstanden ist, kann ein darauf basierender deklarativer Formalismus sinnvoll
für die Beschreibung von Programmieraufgaben eingesetzt werden.
Das ist vorrangig ein Problem der generischen Anwendung, wenn durch Abstraktionen wie
Prozeduren, Funktionen, Lambdas u. Ä. implizit neue Modelle entstehen. Deren Semantik
wird vor allem durch die Namen transportiert, die deshalb besonders sorgfältig gewählt
werden müssen. Vorsicht ist geboten, wenn Konzepte zu kompliziert oder zu innovativ sind,
um mit einer Bezeichnung treffend charakterisiert werden zu können.
Existenz von Verfahren zur Operationalisierung
Deklarative Programmierung kann erfolgversprechend nur für Domänen und Modelle
eingesetzt werden, für die hinreichend allgemeine Algorithmen für die effiziente
Operationalisierung existieren. Sobald es notwendig ist, die deklarativen Ausdrücke um
Hinweise für die Operationalisierung zu ergänzen, muss gefragt werden, ob ein prozeduraler
Ansatz nicht geeigneter wäre.
Die Existenz der Verfahren ist auf die Dauer nicht hinreichend. Da Projekte bekanntlich
wachsen, sind Skalierbarkeit und Interoperationalität ebenfalls wichtige Faktoren.
Insbesondere die bei der Operationalisierung entstehenden wechselseitigen und zeitlichen
Abhängigkeiten bergen das Potential überproportional schnell zuzunehmen. Dass diese
Gefahr auch außerhalb der IT besteht und schnell alle anderen in den Hintergrund rücken
kann, zeigt der Blick auf einige aktuelle Großbaustellen. (Bauen ist ein lange praktiziertes
Paradebeispiel für deklarative Programmierung in der materiellen Welt. Es illustriert ebenso
gut die Probleme, die bei der Operationalisierung, d. h. der Bauausführung auftreten
können.)
Kombinierbarkeit mit prozeduralen Ansätzen
Wegen der unvermeidbaren Beschränkungen sind klare Schnittstellen wichtig.
Anforderungen, die nicht im modellierten Bereich liegen, können dann sauber abgetrennt
beschrieben werden. Allerdings müssen sich die zu lösenden Aufgaben sinnvoll in
entsprechende Teile zerlegen lassen, um häufige Wechsel der Beschreibungsebene zu
vermeiden.
Der Umgang mit Grenzen der deklarativen Beschreibung muss von Anfang an Bestandteil
des Entwurfs sein, um der späteren Degeneration des Formalismus vorzubeugen.
Zusammenfassung
Wenn diese prinzipiellen Beschränkungen beachtet werden, ein kommunizierbares
Basismodell existiert und ausreichend viele Anwendungsfälle vollständig innerhalb dieses
Modells formalisiert werden können, ist das deklarative Paradigma ein nützliches Werkzeug.
Wenn diese Voraussetzungen nicht erfüllt sind, ist es ein ziemlich sicherer Weg in die
Probleme, die man eigentlich vermeiden wollte.
Das bedeutet speziell für die eingangs erwähnten Java-Erweiterungen, dass sie sorgsam
und nicht nur unter dem Gesichtspunkt kompakteren Codes verwendet werden sollten. Ein
IST DEKLARATIV WIRKLICH INSTRUKTIV
16
Lambda-Ausdruck verbessert die Lesbarkeit nur dann, wenn seine Bedeutung unmittelbar
einsichtig ist. Andernfalls ist die explizite Schreibweise instruktiver.
Besonders groß ist die Gefahr der deklarativen Überfrachtung bei Annotationen. Sie machen
es sehr einfach, verteilte Spezifikationen ohne abgegrenztes semantisches Modell zu
kreieren, was vor allem für die Wartung enorme Risiken schafft. Leider scheint diese
Erkenntnis angesichts der technischen Machbarkeit3 bei vielen Entwicklern von Frameworks
übersehen zu werden.
Schließlich sei noch daran erinnert, dass John Backus bereits 1977 in einer vielbeachteten
Rede [5] leidenschaftlich dafür plädiert hat, den prozeduralen Programmierstil zu
überwinden. Dieser Appell ist nicht ungehört geblieben und hat erheblichen Einfluss auf die
Forschung gehabt. 35 Jahre später sieht man kleine Schritte in dieser Richtung wie die
Lambdas in Java, aber es ist auch klar geworden, dass andere Paradigmen andere
Probleme verursachen. Es gibt neue, oft spezialisiertere Werkzeuge, die - richtig angewandt
- dabei helfen, die immer größer werdenden Aufgaben zu bewältigen. Deklarative
Programmierung ist ein Werkzeug in diesem Kasten.
3
Die Möglichkeiten sind tatsächlich fast unbegrenzt. Man kann nicht nur, wie bereits angewandt, Methodenaufrufe in zusätzlichen Code einbetten. Es ist durchaus machbar, Programmteile selbst in Form von
Annotationen zu schreiben, und diese dann noch auf unterschiedliche Weise ausführen zu lassen. – Was heute
noch sinnlos erscheint, wird früher oder später gemacht werden, einfach weil es gemacht werden kann. Der Geist
ist aus der Flasche und wird sich nicht leicht wieder einfangen lassen.
IST DEKLARATIV WIRKLICH INSTRUKTIV
17
Literaturverzeichnis
[1]
[2]
[3]
[4]
[5]
[6]
https://existentialtype.wordpress.com/2013/07/18/what-if-anything-is-a-declarativelanguage/
http://support.microsoft.com/kb/186133/de
http://infolab.stanford.edu/~ullman/fcdb/oracle/or-plsql.html
http://www.drdobbs.com/jvm/why-build-your-java-projects-with-gradle/240168608
John Backus: Can Programming Be Liberated from the von Neumann Style? A
Functional Style and Its Algebra of Programs. In: Communications of the ACM. Vol.
21, No. 8, August 1978, S. 613–641
The Java Language Specification,. Abschn. 9.7,
http://docs.oracle.com/javase/specs/jls/se8/
Der Autor
Dr. Jürgen Lampe ist IT-Berater bei der Agon Solutions in Frankfurt. Seit
mehr als 15 Jahren befasst er sich mit Design und Implementierung von
Java-Anwendungen im Bankenumfeld. Sein Interesse gilt besonders
Fachsprachen (DSL) und Werkzeugen für deren Implementierung.
Email: [email protected]
Agon Solutions
Die Agon Solutions, 2004 gegründet, ist ein dynamisches und erfolgreiches ITDienstleistungsunternehmen mit Firmensitz in Eschborn bei Frankfurt und weiteren
Standorten in Hamburg und Berlin. Das branchenübergreifende Dienstleistungsportfolio von
Agon Solutions umfasst das „Agon IT-Consulting“, eine bewährte, herstellerneutrale ITBeratung; sowie die „Agon IT-Solutions“, zu denen maßgeschneiderte, individuelle
Softwareentwicklung und passgenaue Softwareintegration gehören. Agon Solutions stellt
sich individuell auf seine Kunden ein und setzt auf professionelles Projektmanagement,
proaktives Anforderungsmanagement sowie änderbare Softwarearchitekturen („Design for
Change“) – für Ergebnisse von besonders hoher Qualität. Die Stärken liegen in der
Integration und Migration von Systemen, der Vernetzung von Mainframe-, Java-Backendund Web-Frontend-Anwendungen und der Architekturberatung von Software-Landschaften.
In ausgewählten Branchen wie Banken, Versicherungen, Touristik/Aviation und Health Care
bietet Agon Solutions gemeinsam mit seinen Partnern Lösungen, die auf fundiertem
Geschäftsprozess-Know-how beruhen und speziell auf die jeweilige Branche zugeschnitten
sind: die „Agon Business Solutions“. Die plattformübergreifende technologische Kompetenz
bei Agon Solutions reicht von klassischen Mainframe-Anwendungen bis hin zu modernen
Java/JEE Web- und Portal-Architekturen. Zu den Referenzkunden von Agon Solutions
IST DEKLARATIV WIRKLICH INSTRUKTIV
18
gehören unter anderen die die Commerzbank, die BMW Bank - Segment Financial Service,
die Deutsche Börse, die Finanz Informatik, die Provinzial Nordwest, die Deutsche Lufthansa,
die Thomas Cook und die AOK Berlin-Brandenburg.
Copyright:
A:gon Solutions GmbH
Frankfurter Strasse 71-75
D-65760 Eschborn
Telefon : +49 6196 80269 0
Telefax : +49 6196 80269 11
http://www.agon-solutions.de
Handelsregister Frankfurt HRB 58185
St.-Nr. 4022826171
Geschäftsführer: Udo Peters
IST DEKLARATIV WIRKLICH INSTRUKTIV
19
Herunterladen