Sprachmerkmale im Vergleich


... [Informatikseminar 2002] ... [Beispiele in Haskell und Java] ... [Anwendung: Prototyping]

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.

Quicksort in Haskell

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]

Quicksort in C

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

 


... [Informatikseminar 2002] ... [Beispiele in Haskell und Java] ... [Anwendung: Prototyping]