Im Folgenden eine kurze Zusammenfassung der sprachlichen Merkmale von Haskell und Java im Vergleich, soweit sie mir aufgefallen sind.
Die algorithmische Herangehensweise ist bei funktionalen Programmiersprachen vor allen Dingen geprägt durch die Beschreibung des "was", also des Problems, welches berechnet werden soll. Im Gegensatz dazu beschreiben imperative, also auch objekt-orientierte Algorithmenimplementationen eher "wie" ein Problem gelöst werden soll. Dies wird besonders im folgenden Quicksort-Beispiel deutlich, welches von der Haskell-Homepage stammt.
qsort [] = []
qsort (x:xs) = qsort elts_lt_x ++ [x] ++ qsort elts_greq_x where elts_lt_x = [y | y <- xs, y < x] elts_greq_x = [y | y <- xs, y >= x]
qsort( a, lo, hi ) int a[], hi, lo; { int h, l, p, t; if (lo < hi) { l = lo; h = hi; p = a[hi]; do { while ((l < h) && (a[l] <= p)) l = l+1; while ((h > l) && (a[h] >= p)) h = h-1; if (l < h) { t = a[l]; a[l] = a[h]; a[h] = t; } } while (l < h); t = a[l]; a[l] = a[hi]; a[hi] = t; qsort( a, lo, l-1 ); qsort( a, l+1, hi ); } }
Dabei ist es hier erstmal unerheblich, ob C oder Java verwendet wird, der Ansatz
liesse sich in Java ähnlich implementieren.
Der imperative Ansatz ist sehr wahrscheinlich performanter, da der Algorithmus
direkt auf den Eingangsdaten arbeiten kann.
Dem ist die Verständlichkeit der Haskell-Implementierung entgegenzusetzen.
Aufgrund von formalen Methoden (Lambda-Kalkül), als auch der Prägnanz
des Haskellcodes ist der erste Algorithmus leichter auf Korrektheit zu überprüfen.
Dabei hilft die 'referentielle Transparenz' der Haskell-Programme: Bezeichner
stehen immer für ihren Wert und lassen sich überall im Programm gegen
diesen Wert austauschen und umgekehrt.
Auf der anderen Seite (Java) haben wir Seiteneffekte, der Wert einer Variablen
kann also vom Zustand des Programms abhängen. Diese 'Zustandstransformationen'
sorgen für einen grossen Teil der Fehler. Ein Objekt-Orientiertes Programm
könnte auch als 'ein grosser Seiteneffekt' bezeichnet werden.
Auch die Codelänge ist zu beachten. Unter anderem durch das Wegfallen von
Zuweisungen, die einen erheblichen Teil des Programmcodes bei imperativen Programmen
darstellen, reduziert sich der Codeumfang um bis zu 90 Prozent. [3]
Die Nutzung von Higher-Order-Functions und Polymorphie ermöglicht die
Verfassung von sehr allgemeinen Algorithmen. Das macht Programmstücke erheblich
wiederverwendbarer und erhöht damit die Softwarequalität [4].
Ob Java dies mit der Generic Java Extension ausgleichen kann, bleibt abzuwarten,
da das Konzept u.U. sehr aufgeweicht werden könnte.
Higher-Order-Functions werden nur möglich durch die Nutzung von Funktionen
als Werte erstes Ordung, wohingegen Javaprogramme zur Erreichung der
gleichen Funktionalität die Funktionen erst in Objekte "verpacken"
muss.
Auch die Verwendung von Listen und Tupeln als direktes Merkmal der Sprache, sowie die Verwendung von List-Comprehension und Standardfunktionen machen die Haskellprogrammierung deutlich produktiver, was auch das folgende Fallbeispiel zeigt.
Automatisches Speichermanagment beherschen beide Sprachen, Haskell prinzipbedingt natürlich wesentlich umfassender, da es den Begriff des 'Speichers' in funktionalen Programmiersprachen nicht gibt.
Syntaktische Hilfsmittel, wie Pattern-Matching und die Layout-Regel ("Offside-Rule") machen den Haskell-Quelltext im Vergleich wesentlichen kompakter, was der Prägnanz und Verständlichkeit entgegenkommt.
Nicht unerwähnt sollte bleiben, dass funktionale Algorithmen evtl. schwieriger zu entwickeln sind, und am Anfang mehr Compilerfehler auswerfen. Das gleicht sich evtl. durch ein stabileres, weil einfacher zu überblickendes Ergebniss wieder aus. Das lässt sich auch auf das strenge Typsystem von Haskell zurückführen, welches viele Fehlersituationen von vornerein verbietet.
Grundsätzlich ist es natürlich auch in imperativen/OO-Sprachen möglich funktional zu programmieren. Mir hilft es dabei vielleicht einen Moment länger über einen gewählten Algorithmus nachzudenken, um evtl. ein einfacheren (oder deterministischeren) Algorithmus zu bekommen.
Hier noch einmal eine tabellarische Gegenüberstellung der sprachlichen Merkmale (soweit sie sich mir erschlossen):
Zusammenfassung:
Ziel des Programmierers ist u. A. ein modulares, einfaches, mögl. generelles Programm zu entwickeln