Rein ins VergnĂĽgen
Bei Programmierern erfreut sich das funktionale Paradigma wachsender Beliebtheit. Das hat zur Entwicklung einer Reihe neuer Sprachen geführt. Eine davon ist Pure – eine Kreuzung aus Haskell und Lisp.
- Michael Riepe
Funktionale Programmiersprachen sind wieder „in“ – siehe den Artikel „Renaissance des F“ auf Seite 54 der Printausgabe. Zu den jüngeren Vertretern der Gattung gehört Pure (siehe Kasten „Onlinequellen“). Es lehnt sich syntaktisch an Haskell an, soll jedoch einfacher zu lernen sein. Pure kennt – ähnlich wie Lisp – keine Typdeklarationen, wohl jedoch unterschiedliche Datentypen wie Integer-, Gleitkomma-, rationale und komplexe Zahlen, Strings, Listen, Tupel oder Matrizen nebst den dazugehörigen Operatoren. Das macht die Sprache vor allem für wissenschaftliche Anwendungen interessant.
Onlinequellen
Pure basiert im Gegensatz zu Lisp jedoch nicht auf dem Lambda-Kalkül, sondern auf der aus der theoretischen Informatik bekannten, nicht weniger mächtigen Termersetzung (term rewriting). Als Ergänzungen bietet es jedoch auch Lambda-Funktionen, lokale Funktionen, lokale und globale Variablen sowie die Möglichkeit, Makros zu definieren. Darüber hinaus beherrscht Pure fortgeschrittene Konstrukte wie Exceptions oder Futures.
Eine einfache Implementierung der Fibonacci-Funktion sieht in Pure ähnlich aus wie deren mathematische Definition:
fib 0 = 0;
fib 1 = 1;
fib n = fib (n - 2) + fib (n - 1)
Anders als in Haskell muss der Programmierer jede Regel mit einem Semikolon abschlieĂźen. Gibt man im Interpreter anschlieĂźend den Ausdruck fib 9; ein, generiert pure mithilfe des Compiler-Frameworks LLVM (Low Level Virtual Machine) ein StĂĽck Maschinencode, dessen AusfĂĽhrung die Antwort 34 liefert. Daher ist Pure relativ schnell.
Wie bei allen Sprachen, die Rekursion statt Iteration verwenden, sollte der Programmierer die Begrenzung des Stacks beachten. Die Fakultätsfunktion
fak n = if n>0 then n*fak (n-1) else 1
etwa bringt den Stack bei größeren Argumenten zum Überlaufen und lässt den Interpreter mit einem Segmentation Fault abstürzen. Eine Überprüfung des Stacks führt pure nur durch, wenn man vor dem Programmstart mit export PURE_STACK=<kbytes> eine Maximalgröße festlegt. In dem Fall löst eine zu tiefe Rekursion eine Exception aus:
> fak 1000000L;
<stdin>, line 6: unhandled exception 'stack_fault' while evaluating 'fak 1000000L'
<stdin>, line 6: unhandled exception 'stack_fault' while evaluating 'fak 1000000L'
Abhilfe schafft die Verwendung einer endrekursiven lokalen Hilfsfunktion:
fak n = fak2 n 1
with fak2 n r = if n>0 then fak2 (n-1) (n*r) else r;
end
Analog lassen sich mit when … end lokale Variablen definieren. Wer globale Variablen benötigt, kann mit let <variable> = <wert> welche definieren beziehungsweise ihren Wert ändern. Ein Ausdruck der Form const <konstante> = <wert> dient zum Definieren von Konstanten.
Anonyme Funktionen lassen sich in Pure als Lambda-Ausdrücke schreiben. Die Funktion \x –> x+1 etwa erhöht ihr Argument um 1. In der Regel muss man den Lambda-Ausdruck jedoch in Klammern einschließen, da der Operator –> eine niedrige Priorität besitzt: map (\x –> x+1) [1,2,3]; etwa wendet die Funktion auf die Elemente einer Liste an.
Umfangreiche Bibliothek
Pure-Programme können auf die Funktionen der C-Laufzeitbibliothek zurückgreifen. Wer etwa trigonometrische Funktionen benötigt, kann nach der Deklaration extern double sin(double); die Sinusfunktionen der libm verwenden. Allerdings muss er Argumente gegebenenfalls erst in Gleitkommazahlen umwandeln, da Pure keine automatische Wandlung durchführt.
Abhilfe schafft eine Bibliothek, die sich mit using math; einbinden lässt und die sowohl die mathematischen Funktionen deklariert als auch passende Regeln für das automatische Umwandeln der Argumente. Außerdem bringt die Sprache weitere Module für die Arbeit mit Arrays, Dictionaries, Heaps, Matrizen, Mengen (Sets und Bags) sowie Strings mit. Systemnahe Funktionen lassen sich mit using system; einbinden. Ein guter Teil der Sprache selbst ist im Modul prelude definiert, das der Interpreter beim Start automatisch lädt.
Pure lässt sich auch als Skriptsprache nutzen. Das Kommando pure <dateien> lädt die angegebenen Dateien und führt sie aus. Will man anschließend in den interaktiven Modus wechseln, muss man pure –i aufrufen. Die Option –g schaltet den integrierten Debugger ein.
Wer ein Skript mit Argumenten aufrufen will, sollte das Kommando pure –x skript.pure <argumente> verwenden. Mit pure –c lassen sich Skripte jedoch auch in ausführbare Programme kompilieren. Das setzt allerdings voraus, dass die komplette LLVM-Toolchain zur Verfügung steht. Fehlt sie, kann man mit dem Kommando pure –c skript.pure –o skript.ll Code für den LLVM-Assembler erzeugen und diesen auf einem anderen System weiterverarbeiten.