Grundlagen der funktionalen Programmierung
Wenn Sie bereits die ersten Programmierkenntnisse gesammelt haben, dann kennen Sie sicherlich schon die wichtigsten Elemente, die hierbei zum Einsatz kommen. Von besonderer Bedeutung bei der Programmerstellung ist es, Variablen Werte zuzuweisen. Diese Werte können sich dann während des Programmablaufs immer wieder verändern. Für die Steuerung kommen häufig if-Abfragen zum Einsatz, die eine Bedingung überprüfen. Auch Schleifen stellen ein wichtiges Steuerungselement dar, indem sie es erlauben, einen Befehl zu wiederholen.
Allerdings gibt es auch Programme, die auf die genannten Elemente verzichten. Das führt nicht nur zu einem komplett anderen Aufbau. Auch die grundsätzliche Herangehensweise an informatische Probleme ist dabei verschieden. Eine wichtige Möglichkeit stellt beispielsweise die funktionale Programmierung dar. Die Verwendung funktionaler Programmiersprachen ist zwar nicht allzu weit verbreitet. Doch bietet sie in manchen Situationen erhebliche Vorteile. Beispielsweise lassen sich die dabei entstehenden Programme deutlich einfacher auf Fehler überprüfen. Daher sind sie meistens robuster. Es kommt hinzu, dass in manchen Fällen die Codeerstellung ausgesprochen effizient ist. Manche Probleme, die bei der Verwendung einer imperativen Programmiersprache Hunderte Codezeilen erfordern, lassen sich mit einem funktionalen Programm mit deutlich weniger Befehlen lösen. Dieser Artikel stellt die Technik der funktionalen Programmierung vor.
Was versteht man unter funktionaler Programmierung?
Wie die Bezeichnung funktionale Programmierung bereits vermuten lässt, spielen hierbei Funktionen eine wichtige Rolle. Nun könnte man einwenden dass viele bekannte Programmiersprachen das Erstellen von Funktionen ebenfalls erlauben – wie etwa C, C++ oder Python. Dennoch bedeutet das nicht, dass es sich hierbei automatisch auch um funktionale Programmiersprachen handelt. Die Möglichkeit, Funktionen zu erstellen, ist zwar eine wichtige Voraussetzung dafür, dass man eine Programmiersprache als funktional bezeichnen kann. Allerdings muss sie hierfür noch einige weitere Bedingungen erfüllen.
Beispielsweise ist es notwendig, dass die Funktion gegenüber allen übrigen Daten gleichberechtigt ist. Man spricht hierbei häufig auch von First-Class-Functions. Das heißt, dass Funktionen auch als Parameter an andere Funktionen übergeben werden können. Außerdem kann eine Funktion als Ergebnis eine weitere Funktion ausgeben. Funktionale Programmiersprachen müssen es erlauben, Funktionen erst zur Laufzeit zu erstellen – genau wie dies bei allen übrigen Datenobjekten der Fall ist.
Darüber hinaus wird hierbei der komplette Programmablauf als eine Funktion verstanden. Diese hat eine Eingabe und eine Ausgabe. Das Ergebnis hängt ausschließlich von der Eingabe ab und wird nicht von anderen Daten beeinflusst. Die Funktionen selbst stellen keine Folge von Anweisungen dar. Vielmehr bestehen sie selbst wieder aus Funktionsaufrufen.
Deklarative und imperative Programmierung: Worin bestehen die Unterschiede?
Um die funktionale Programmierung besser zu verstehen, ist es sinnvoll, zunächst auf die Unterschiede zwischen der deklarativen und der imperativen Programmierung einzugehen. Bei den meisten gängigen Programmiersprachen – beispielsweise bei C, C++, C#, Python, Java, PHP oder Perl – handelt es sich um imperative Programmiersprachen. Diese verwenden vorwiegend die bereits in der Einleitung beschriebenen Elemente für die Programmgestaltung: Zuweisungen, if-Abfragen und Schleifen.
Die imperative Programmierung verändert die Zustände eines Programms. Sie gibt hierfür Schritt für Schritt Anweisungen, wie diese Zustände beeinflusst werden sollen. Die Veränderung dieser Zustände führen schließlich zum gewünschten Ergebnis. Das zeigt, dass hierbei die Frage im Vordergrund steht, wie ein Problem gelöst werden soll. Das Programm gibt hierfür präzise Anweisungen.
Die deklarative Programmierung befasst sich hingegen in erster Linie mit der Frage, was das Programm erreichen soll. Sie gibt also eine genaue Vorgabe, wie das Ergebnis aussehen soll. Um diese Vorgehensweise zu verstehen, kann man die Datenbanksprache SQL heranziehen – eines der wichtigsten Beispiele für die deklarative Vorgehensweise. Wenn man hierbei beispielsweise den SELECT-Befehl anwendet, um Werte aus einer Datenbank abzurufen, muss man ganz konkrete Angaben dazu machen, welche Eigenschaften diese aufweisen sollen. Sie müssen daher angeben, was Sie aus der Datenbank abrufen möchten. Wie diese Aufgabe umgesetzt wird, spielt hingegen für Sie als Anwender keine Rolle. Diese Aufgabe übernehmen die internen Prozesse des Datenbanksystems.
Die funktionale Programmierung verfolgt die deklarative Herangehensweise. Sie gibt demnach keine genauen Vorgaben für den Programmablauf. Vielmehr machen Sie hierbei Vorgaben, was Sie mit der entsprechenden Funktion erreichen möchten.
Wichtige Aspekte der funktionalen Programmierung
Nach diesen Vorüberlegungen, können wir nun auf die praktischen Aspekte der funktionalen Programmierung eingehen. Einer der wesentlichen Unterschiede besteht darin, dass Sie in einem funktionalen Programm nicht die Zustände von Variablen verändern können. Während diese Aufgabe in imperativen Programmen den zentralen Lösungsansatz darstellt, ist diese Möglichkeit in funktionalen Programmen nicht gegeben. Hier haben Variablen einen festen Wert und können ihren Zustand nicht verändern. Das komplette Programm orientiert sich an mathematischen Funktionen.
Die Unmöglichkeit, den Zustand einer Variable zu verändern, führt dazu, dass viele Elemente der imperativen Programmierung hierbei nicht sinnvoll einzusetzen sind. Schleifen oder if-Abfragen zu verwenden, ergibt beispielsweise nur dann Sinn, wenn Sie hierbei die Zustände der Variablen verändern können. Stattdessen kommen in funktionalen Programmen Funktionen zum Einsatz. Diese sind häufig ineinander verschachtelt. Auch der Rekursion – also einer Funktion, die sich selbst aufruft – kommt in diesem Bereich eine wichtige Bedeutung zu.
Ein weiterer wichtiger Unterschied zur imperativen Programmierung besteht darin, dass der zeitliche Ablauf hierbei nur eine sehr geringe Bedeutung hat. Bei imperativen Zuweisungen kann sich der Zustand einer Variable immer wieder ändern. Daher ist der Zeitpunkt, zu dem Sie auf sie zugreifen, von fundamentaler Bedeutung. Bei funktionalen Programmen verändern sich die Zustände hingegen nicht. Das führt dazu, dass der zeitliche Ablauf hierbei kaum eine Rolle spielt.
Welche Vor- und Nachteile bietet die funktionale Programmierung?
Schließlich ist es sinnvoll, einen Blick auf die Vor- und Nachteile der funktionalen Programmierung zu werfen. Wenn Sie sich mit diesem Thema befassen, werden Sie sicherlich feststellen, dass es sich hierbei um ein recht komplexes Thema handelt. Die funktionale Programmierung ist insbesondere für Anfänger nicht ganz einfach zu verstehen. Daher fällt der Einstieg hierbei meistens deutlich schwerer als bei der imperativen Programmierung.
Darüber hinaus müssen Sie beachten, dass die Verwendung der funktionalen Programmierung nicht bei allen Aufgaben sinnvoll ist. Beispielsweise bei Programmen, die eine Verbindung zu einem Server oder zu einer Datenbank aufbauen sollen, stellt die Verwendung der funktionalen Programmierung keine gute Wahl dar. Auch bei der Bearbeitung großer Datenmengen ist der Einsatz funktionaler Programme nicht zu empfehlen. Der Grund dafür besteht darin, dass die internen Rechenprozesse hierbei nicht allzu effizient ablaufen. Das führt bei großen Datenmengen zu einer schlechten Performance.
Dennoch bietet die funktionale Programmierung auch viele Vorteile. Von großer Bedeutung ist beispielsweise die sehr effiziente Codeerstellung. Aufgaben, für die imperative Programme Dutzende Zeilen benötigen, können Sie häufig mit einem funktionalen Programm mit nur zwei oder drei Codezeilen lösen. Das reduziert den Aufwand bei der Programmerstellung deutlich. Ein weiterer Aspekt besteht darin, dass funktionale Programme einfach zu überprüfen sind. Eine Funktion, bei der das Ergebnis lediglich von den vorgegebenen Parametern abhängt, lässt sich deutlich einfacher testen, als wenn weitere Zustände Einfluss auf sie nehmen. Außerdem ist es hierbei deutlich einfacher, einen mathematischen Beweis dafür zu führen, dass eine Funktion ihre Aufgabe korrekt erledigt.
Da der zeitliche Ablauf bei der funktionalen Programmierung eine untergeordnete Rolle spielt, eignen sich diese Programme außerdem hervorragend für eine parallele Ausführung. Das ermöglicht es insbesondere, Prozessoren mit mehreren Kernen effizient für ein einzelnes Programm auszunutzen.
Einer der wichtigsten Vorteile der funktionalen Programmierung besteht jedoch darin, dass Sie hierbei unerwünschte Seiteneffekte verhindern. Bei imperativen Programmen, bei denen sich die Zustände der Variablen ändern können, kommt es gelegentlich vor, dass ein Wert in unterschiedlichen Programmbereichen bearbeitet wird. Das kann zu unerwünschten Effekten und damit zu einem fehlerhaften Programmablauf führen. Da es in der funktionalen Programmierung nicht möglich ist, diese Zustände zu verändern, werden hierbei Seiteneffekte zuverlässig ausgeschlossen. Diese Eigenschaft, dass eine Variable zu jedem beliebigen Zeitpunkt den gleichen Wert aufweist, wird auch als referenzielle Transparenz bezeichnet. Das führt zu besonders robusten Programmen.
Anwendungsmöglichkeiten für die funktionale Programmierung
Die Grundlagen der funktionalen Programmierung wurden bereits in den 1930er Jahren entwickelt. Dabei handelte es sich zunächst nur um theoretische Überlegungen. Als die ersten Computer entstanden, spielte die funktionale Herangehensweise jedoch bereits eine wichtige Rolle.
Allerdings führte die funktionale Programmierung über viele Jahrzehnte hinweg ein Schattendasein. Sie kam in erster Linie im akademischen Bereich zum Einsatz. Funktionale Programme entstanden vorwiegend an den Universitäten und nicht bei praktischen Anwendungen. Für kommerzielle Produkte kam fast ausschließlich die imperative Programmierung zum Einsatz.
Allerdings kam es in diesem Bereich zu einem starken Umdenken. Die funktionale Programmierung findet immer mehr Anhänger. Das zeigt sich auch daran, dass immer mehr Programmiersprachen, die eigentlich nach dem Paradigma der imperativen Programmierung erstellt sind, auch die funktionale Programmierung unterstützen.
Einer der wesentlichen Gründe hierfür besteht darin, dass sich die funktionale Programmierung als sehr hilfreich für die Umsetzung der künstlichen Intelligenz erwies. KI-Anwendungen lassen sich auf diese Weise häufig sehr gut umsetzen. Das liegt in erster Linie daran, dass KI im Wesentlichen auf mathematischen Modellen beruht. Diese lassen sich mit funktionalen Programmen besonders gut nachstellen. Auch die referenzielle Transparenz und die parallele Ausführung der Prozesse wirkt sich hierbei positiv aus. Das führt dazu, dass die funktionale Programmierung nun auch in der Praxis immer stärker an Bedeutung gewinnt.
Funktionale Programmiersprachen
Zum Abschluss soll noch ein Blick auf die funktionalen Programmiersprachen geworfen werden. Der erste Vertreter in diesem Bereich war LISP. Darüber hinaus gibt es jedoch noch viele weitere Programmiersprachen, die speziell für die funktionale Programmierung entwickelt wurden – wie etwa ML, OCaml, F#, Clojure, Erlang und Scala. Eine herausragende Stellung nimmt hierbei jedoch die Programmiersprache Haskell ein. Diese kommt insbesondere im akademischen Bereich häufig zum Einsatz. Hierbei handelt es sich um eine der wichtigsten rein funktionalen Programmiersprachen.
Da das funktionale Programmierparadigma in den letzten Jahren stark an Bedeutung gewonnen hat, haben auch zahlreiche imperative Sprachen dieses aufgegriffen. Beispielsweise bieten mittlerweile auch C++, Java und Python Möglichkeiten für die funktionale Programmgestaltung. Allerdings ist hierbei nach wie vor erkennbar, dass diese Sprachen nicht von Grund auf für diese Vorgehensweise entwickelt wurden. Das führt häufig zu einer komplizierteren Syntax, die zur Folge hat, dass der Code nicht so klar und kompakt ist wie etwa bei LISP oder Haskell.
Danke, sehr gut beschrieben