Funktionale Programmierung

2. Februar 2017Karl Brodowsky

Innerhalb der Informatik sind wir es gewohnt, dass immer wieder eine neue Idee aufkommt, mit der sich „alle“ Probleme leicht lösen lassen. Dies kann sich beispielsweise in neuen Programmierparadigmen – wie etwa OO oder jetzt FP – neuen Datenbanktechnologien, Betriebssystemen, Architekturen oder Frameworks ausdrücken. Interessant ist aber die Frage, was uns diese Technologien und Paradigmen − heute als „funktionale Programmierung“ bezeichnet − jenseits der Lösung aller Probleme wirklich bringen.

Was leistet funktionale Programmierung?

Zunächst scheint der Begriff „funktionale Programmierung“ schwer greifbar zu sein. Aber schauen wir uns einmal an, ob C − eine der am weitesten verbreiteten Programmiersprachen − eine funktionale Programmiersprache ist, oder zumindest eine funktionale Programmierung erlaubt. C-Programme bestehen aus Funktionen, die sich gegenseitig aufrufen. Es ist ebenfalls möglich, Funktionen höherer Ordnung zu schreiben, die als Parameter oder Rückgabewerte bestimmte Funktionen haben. Wird mit C programmiert, ist dies ein sehr mächtiges Hilfsmittel. Allerdings reicht es noch nicht aus, um hier von einer funktionalen Programmiersprache zu sprechen.

Eine Funktion im Sinne der funktionalen Programmierung ist vergleichbar mit einer mathematischen Funktion: sin(x) gibt für dasselbe x immer denselben Wert aus. Funktionen, die wir in üblichen Programmiersprachen − auch in C − schreiben, können Seiteneffekte haben. Allerdings existieren Konstrukte, die diese Seiteneffekte auslagern, begrenzen oder kontrollieren. Die eigentliche Programmlogik kommt dann ohne Seiteneffekte aus. Jedoch haben Programmierer auch das Bedürfnis, Zwischenergebnisse zu speichern oder einmal berechnete Werte für weitere Berechnungen aufzubewahren. Dafür bieten funktionale Programmiersprachen sogenannte „lazy evaluation“ an. Der Programmierer hat somit die Möglichkeit, Zwischenergebnisse in einer Konstante abzulegen. Der tatsächliche Wert dieser Konstante wird aber erst beim ersten Gebrauch ermittelt. Das kann auch feingranularer − beispielsweise für eine Liste, bei der die einzelnen Elemente erst bei Bedarf berechnet werden − erfolgen. Entscheidend ist, dass in rein funktionalen Sprachen alle Objekte „immutable“ sind, also nicht mehr verändert werden können, wenn sie einmal erstellt wurden. Um ein Beispiel zu nennen: Das Hinzufügen eines Elements zu einer Liste bedeutet, dass diese Liste unverändert bleibt. Jedoch wird eine Kopie erstellt, die zusätzlich das neu hinzugefügte Element enthält.

Der Vorteil besteht darin, dass Objekte problemlos zwischen Threads austauschbar sind und mehrmals genutzt werden können, wenn sie einmal erstellt wurden.

Mit der Programmiersprache C ist zwar ein Nachbau möglich, allerdings ist die benötigte Infrastruktur nicht vorhanden. Damit wird es schwierig, die „unerlaubten Seiteneffekte“ von den „erlaubten Seiteneffekten“ zu unterscheiden.

Ein weiterer Aspekt ist, dass man in funktionalen Sprachen typischerweise Werte aus der Umgebung, in der sie definiert werden, referenzieren kann. Dafür wird der Begriff „Closure“ verwendet. Zur Veranschaulichung dient das folgende Beispiel: Es soll eine Funktion createAdder(x) geschrieben werden, die zum gegebenen x eine Funktion adder(y) zurückgibt. Letztere berechnet wiederum zum gegebenen y die Summe x+y:

Dies ist in Sprachen, die funktionale Programmierung unterstützen, leicht umsetzbar. Auch in den Skript-Sprachen Perl oder Ruby ist ein solches Vorgehen möglich:

Clojure:

Es ist zu erkennen, dass das x in die anonyme Funktion „eingebacken“ wird.

Scala:

Perl:

Java8:

Auch in Java kann dies relativ einfach erreicht werden und zwar, wenn die Funktion einfach in eine anonyme innere Klasse gepackt wird. Die Programmiersprache C erlaubt ebenfalls ein solches Vorgehen, jedoch gestaltet sich der Vorgang hier ein wenig schwieriger.

Wie können wir davon profitieren?

Soll eine Applikation komplett funktional − ohne veränderbare Werte und Seiteneffekt − entwickelt werden, ist das für Multithreading sehr hilfreich. Alles, was unsere Thread-Safety stören könnte, ist nämlich eliminiert. Auch für nicht (rein-)funktionale Sprachen können wir an dieser Stelle etwas dazu lernen: Es ist gut, veränderbare Objekte zu vermeiden, wenn es ohne oder mit wenig Zusatzaufwand möglich ist. Bei Objekten, die als Parameter oder Rückgabewerte von Funktionen und Methoden auftreten, ist es sinnvoll, wenn sie sich im System bewegen und an mehreren Orten gleichzeitig zugänglich sind. Wenn sie unveränderlich sind und auch nicht über eine „Backdoor“ für Veränderungen verfügen − wie eine in unmodifiable gewrappte Java-Collection – stellt dies kein Problem dar.

Fazit

Festzuhalten bleibt, dass Konzepte der funktionalen Programmierung ein interessanter und nützlicher Teil des Werkzeugkastens eines Programmierers sind. Es wird spannend sein, zu beobachten, wohin diese Ideen in den nächsten Jahren führen werden.

Habt ihr bereits Erfahrungen mit funktionaler Programmierung gemacht oder Fragen zu meinem Artikel? Ich freue mich auf eure Kommentare.

Karl Brodowsky Karl Brodowsky ist Softwarearchitekt und -entwickler bei adesso Schweiz und befasst sich vor allem mit Java, Ruby, Perl, C, Scala und Clojure. Zurzeit ist er als Payroller in einem Projekt für den Kanton Basel-Stadt im Einsatz.
Artikel bewerten:
1 Star2 Stars3 Stars4 Stars5 Stars
Loading...

Kommentar hinzufügen:

Ihr Kommentar: