E-Book, Deutsch, 187 Seiten
Reihe: Rheinwerk Computing
Widman Funktionale Programmierung verstehen
1. Auflage 2024
ISBN: 978-3-8362-9870-4
Verlag: Rheinwerk
Format: EPUB
Kopierschutz: 0 - No protection
Konzepte und Entwurfsmuster für guten Code
E-Book, Deutsch, 187 Seiten
Reihe: Rheinwerk Computing
ISBN: 978-3-8362-9870-4
Verlag: Rheinwerk
Format: EPUB
Kopierschutz: 0 - No protection
Lernen Sie das Paradigma der funktionalen Programmierung kennen und schreiben Sie damit eleganten, wartbaren Code! Tauchen Sie in eine neue Welt ein, denken und coden Sie auf die funktionale Art . Die meisten Codebeispiele in diesem Buch sind in leicht verständlichem Scala formuliert und außerdem in Python übersetzt. Der Autor Jack Widman geht außerdem auf sprachabhängige Aspekte ein und zeigt dazu auch Codebeispiele in Java, JavaScript und C#. Für Python-Liebhaber gibt es in der deutschen Ausgabe ein exklusives Special mit Python-Codebeispielen und zusätzlichen Tipps, um das Buch ganz im „Python-Modus“ lesen zu können.
Aus dem Inhalt:
- Die Denkweise kennenlernen
- Ein paar einfache mathematische Grundlagen
- Entwurfsmuster
- Datenstrukturen
- Unveränderlichkeit
- Nebenläufigkeit
- Kombination mit OOP
- Besonderheiten verschiedener Programmiersprachen
- Python-Special
Autoren/Hrsg.
Weitere Infos & Material
Vorwort ... 9
1. Was ist funktionale Programmierung? ... 11
1.1 ... Unveränderlichkeit ... 13
1.2 ... Referenzielle Transparenz ... 16
1.3 ... Funktionen höherer Ordnung ... 18
1.4 ... Lazy Evaluation ... 19
1.5 ... Funktionale Denkweise ... 21
1.6 ... Die Vorteile der funktionalen Programmierung ... 22
1.7 ... Fazit ... 26
2. Mathematische Grundlagen ... 27
2.1 ... Mengenlehre ... 27
2.2 ... Grundlagen der Informatik ... 32
2.3 ... Fazit ... 34
3. Kategorientheorie und Entwurfsmuster ... 35
3.1 ... Kategorientheorie und Funktionsmuster ... 37
3.2 ... Funktionsmuster ... 51
3.3 ... Fazit ... 60
4. Funktionale Datenstrukturen ... 61
4.1 ... Die Option-Datenstruktur ... 62
4.2 ... Die Try-Datenstruktur ... 67
4.3 ... Die Either-Datenstruktur ... 68
4.4 ... Funktionen höherer Ordnung ... 70
4.5 ... Monaden und Scala-Abstraktionen ... 72
4.6 ... Traditionelle Datenstrukturen ... 74
4.7 ... Fazit ... 75
5. Unveränderlichkeit im Detail ... 77
5.1 ... Veränderliche und unveränderliche Variablen ... 77
5.2 ... Rekursion ... 78
5.3 ... Endrekursion ... 86
5.4 ... Weitere Beispiele für die Leistungsfähigkeit der fold-Funktion in Scala ... 91
5.5 ... Verbindung zwischen »fold« und Monoiden ... 92
5.6 ... Vertiefende Informationen zu Funktionen höherer Ordnung ... 96
5.7 ... Fazit ... 101
6. Nebenläufigkeit ... 103
6.1 ... Streams ... 107
6.2 ... Akka-Streams ... 108
6.3 ... Weitere Informationen zu Streams ... 111
6.4 ... FS2: funktionale Streams für Scala ... 112
6.5 ... Fazit ... 114
7. Wie geht es weiter? ... 115
7.1 ... Den reinen Weg einschlagen ... 115
7.2 ... Fazit ... 128
Anhang ... 129
A ... Scala ... 129
B ... Python-Special ... 151
Index ... 183
1 Was ist funktionale Programmierung?
Funktionale Programmierung mag auf den ersten Blick durch Begriffe wie Funktoren, Monoide und Monaden abschreckend wirken, besonders wenn Mathematik nicht zu Ihren Hauptinteressen zählt. Doch keine Sorge, um sich mit FP auseinanderzusetzen, bedarf es keiner mathematischen Expertise.
Die Grundprinzipien der funktionalen Programmierung sind leicht verständlich, solange sie in einer klaren und geradlinigen Art präsentiert werden. Genau das ist das Anliegen dieses Buchs: eine verständliche und praxisnahe Einführung in funktionale Programmierung zu bieten. Vor allem werde ich Ihnen beibringen, wie wir in der funktionalen Programmierung denken. Doch warum sollten Sie sich überhaupt mit funktionaler Programmierung befassen?
Stellen Sie sich vor, es ist 22:00 Uhr, und Sie stecken komplett fest beim Versuch, einen Fehler in einem Programm zu beheben, das Sie am nächsten Morgen liefern müssen. Der Fehler hat offenbar mit einer Variablen namens ratio zu tun. Das Problem ist, dass sich der Wert dieser Variablen ständig ändert, abhängig vom Zustand des modellierten Systems – der Frust steigt.
Ein anderes Szenario: Die Deadline rückt näher, aber in Ihrem Microservice ist ein schwer nachvollziehbarer Fehler aufgetreten, den Sie ausfindig machen müssen. Das Problem scheint in zwei verschachtelten for-Schleifen zu liegen, in denen Variablen auf äußerst komplexe Weise manipuliert werden. Die Logik ist undurchschaubar, und die Lösung will sich einfach nicht zeigen. Hätten Sie doch nur eine Möglichkeit, Programme so zu schreiben, dass sich der Wert von Variablen nicht verändert! Hier kommt funktionale Programmierung ins Spiel.
Variablen, die häufig ihre Werte ändern, sind eine typische Quelle für Programmfehler. Es kann eine Herausforderung sein, den Wert einer solchen Variablen im Blick zu behalten, da er jederzeit mutieren kann.
Aber was genau ist funktionale Programmierung? Was unterscheidet eine funktionale Sprache von einer nicht funktionalen? Tatsächlich hängt dies gewissermaßen davon ab, wie weit Sie gehen möchten. Sie müssen nicht zwangsläufig alle Grundsätze der funktionalen Programmierung befolgen. Der eine Programmierer wird versuchen, alle Prinzipien zu befolgen, die andere Entwicklerin wird sich etwas aussuchen. Das bleibt ganz Ihnen überlassen. Funktionale Programmierung ist ein Paradigma, eine Herangehensweise ans Programmieren – eine Art und Weise, die Welt zu analysieren und in Form von Code wieder zusammenzusetzen. Dies betrifft einerseits die Art, wie wir das zu modellierende Universum organisieren, und andererseits unsere Herangehensweise an die Codestrukturierung.
Um das Wesen der funktionalen Programmierung besser zu verstehen, ist es hilfreich, sie im Vergleich mit der imperativen Programmierung und der objektorientierten Programmierung (OOP) zu betrachten. Es existieren weitere Programmierparadigmen, wie etwa die logische Programmierung, aber die genannten drei sind zweifellos die bekanntesten.
Imperative Programmierung stellt die älteste Form dar – die Programmierung vor dem Aufkommen der objektorientierten und der funktionalen Programmierung. Bei der imperativen Programmierung schreiben Sie Funktionen oder Prozeduren, verwenden for- und while-Schleifen und arbeiten häufig mit Zustandsänderungen. Sprachen wie C oder Pascal sind typische imperative Programmiersprachen. Ein weiteres Paradigma ist die objektorientierte Programmierung (OOP), die derzeit am weitesten verbreitet ist. Hierbei wird die Realität als Ansammlung von Objekten abgebildet. Jedes dieser Objekte besitzt einen eigenen Zustand sowie Methoden, also Aktionen, die ein spezifisches und objektspezifisches Verhalten repräsentieren. Während der Laufzeit des Programms kann sich der Zustand der Objekte ändern. Ein wesentlicher Vorteil dieses Ansatzes ist die Kapselung, wodurch der Zustand und die Methoden eines Objekts auf Codeebene innerhalb des Objekts gebündelt sind. Diese Herangehensweise ist weitaus sinnvoller als die Verteilung des Zustands über den gesamten Code, da die Verwaltung eines variablen Zustands problematisch sein kann. Die Manipulation zahlreicher Variablen, deren Werte sich verändern, erweist sich hierbei als Herausforderung. Im Gegensatz dazu versucht die funktionale Programmierung, veränderbare Zustände zu minimieren oder gänzlich zu umgehen.
Ein grundlegendes Prinzip der funktionalen Programmierung besteht also darin, Zustandsänderungen zu vermeiden, statt sie zu verwalten.
Dennoch ist es nicht immer möglich, komplett auf Zustandsveränderungen zu verzichten. Daher folgt der typische Ansatz in der funktionalen Programmierung dem Konzept, den Codeabschnitt zu isolieren, der für Zustandsänderungen verantwortlich ist. Falls es nicht möglich ist, sämtliche Zustandsänderungen zu umgehen, besteht zumindest die Möglichkeit, den Code, der solche Veränderungen bewirkt, an einer zentralen Stelle zu bündeln.
1.1 Unveränderlichkeit
Der wichtigste Aspekt in der funktionalen Programmierung ist die Unveränderlichkeit. Elemente, die wir nicht in irgendeiner Weise verändern können, gelten als unveränderlich. In der funktionalen Programmierung manifestiert sich diese Unveränderlichkeit auf verschiedene Weisen. Einmal festgelegt, kann der Wert einer Variablen nicht mehr modifiziert werden. Wenn beispielsweise die Variable x zu Beginn eines Programms den Wert 3 aufweist, wird dieser für die gesamte Dauer des Programmlaufs beibehalten. Doch bedeutet das, dass wir im funktionalen Programmierstil keine Möglichkeit haben, Veränderungen wie beispielsweise das Älterwerden einer Person darzustellen? Keineswegs – diese Vorstellung wäre schlicht absurd. Es existieren Techniken, die es uns ermöglichen, den Code zu manipulieren, ohne dabei den Zustand zu verändern.
Betrachten Sie diese einfache for-Schleife in Java, die die Zahlen 0 bis 99 ausgibt:
System.out.println( i );
}
Listing 1.1 »for«-Schleife in Java
Solche Codekonstrukte sind alltäglich. Möglicherweise fragen Sie sich, wie wir sie auf eine unveränderliche Art ausdrücken können. Offensichtlich liegt der Kern dieses Codes in der Veränderung des Werts der Variablen i. Ein gängiger Ansatz in der funktionalen Programmierung ist die Verwendung rekursiver Funktionen – also von Funktionen, die sich selbst aufrufen. Den zuvor gezeigten Code könnten Sie in eine Funktion packen und diese in jeder Iteration für den nächsten Wert von i aufrufen. Ein Beispiel dazu:
if (i > 99) {
return;
}
else {
System.out.println( i)
return f(i+1)
}
}
f(0)
Listing 1.2 Rekursion statt Schleife (Java)
Dieser Code mag zwar etwas ausführlicher sein, doch es erfolgt keine Zustandsveränderung. Wenn Sie bereits ein gewisses Verständnis für funktionale Programmierung besitzen, wissen Sie möglicherweise, dass der Rückgabetyp void ein eindeutiger Hinweis auf Nebeneffekte ist.
Tipp: nach »void« Ausschau halten
In der funktionalen Programmierung sollten alle Funktionen einen Wert zurückgeben. void ist ein sicheres Zeichen für Nebeneffekte.
Zu den Nebeneffekten zählt jegliche Aktivität, die das Programm außerhalb der Funktion beeinflusst – das Schreiben in eine Datei, das Auslösen einer Ausnahme oder die Modifikation einer globalen Variable. Das vorige Codebeispiel veranschaulicht eine Methode, um den Einsatz von zustandsverändernden Operationen zu umgehen. Während Ihrer gesamten Programmierkarriere haben Sie vermutlich Zustandsveränderungen vorgenommen, die Ihnen womöglich unverzichtbar erschienen. Allerdings sollten Sie Folgendes bedenken:
-
Das Mutieren von Zuständen fühlt sich sehr natürlich an.
-
Zustandsänderungen sind eine der Hauptursachen für komplexe Code-Strukturen.
Die erfreuliche Nachricht lautet: Mit ausreichender Übung und Erfahrung wird Ihnen der Ansatz der funktionalen Programmierung genauso vertraut erscheinen wie andere Programmierparadigmen.
Betrachten wir eine weitere Technik zur Vermeidung von Zustandsänderungen. Angenommen, Sie haben ein Objekt mit einer Eigenschaft oder einem Feld, das sich verändert. Die Frage ist nun, wie Sie diese Situation modellieren können, ohne eine Variable im Code zu modifizieren. Betrachten wir zunächst ein Beispiel in Java:
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person person =...




