Daniel Dietrich HSH-Nordbank

Es ist faszinierend zu sehen, wie wenige Bausteine notwendig sind, um den grundlegenden Charakter der objektfunktionalen Programmierung zu skizzieren.

Wir können uns vielfältigen Programmierparadigmen bedienen, um unsere tägliche Arbeit zu verrichten. Jedes Paradigma bietet andere Formen, die es uns gestatten, die Dinge unserer Anschauung sprachlich umsetzen. Die Sprachmittel, die uns zur Verfügung stehen, beeinflussen unsere Wahrnehmung und die Art der Abstraktion. Mächtige Sprachmittel helfen uns, Implementierungen ausdrucksstark zu gestalten und im Idealfall die Angriffsfläche für Fehler zu reduzieren. Vavr erfreut sich recht großer Beliebtheit, nicht nur unter funktionalen Programmierern.

Die imperative Programmierung ist eng angelehnt an die Arbeitsweise der Von-Neumann-Architektur heutiger Computer. Wertzuweisungen, Schleifen und Bedingungen lassen sich direkt auf das Verändern von Speicherinhalten und das Durchführen von Sprüngen bei der Interpretation von Daten übertragen. Spätestens bei der nebenläufigen Programmierung kommen wir hier allerdings schnell an unsere Grenzen, denn es fällt uns schwer, alle Ausführungspfade eines nebenläufigen Programms vorherzusehen. Es bedarf einer Abstraktion mit anderen Sprachmitteln.

Ich möchte anhand des Beispiels der asynchronen Ausführung von Berechnungen zeigen, wie uns objektfunktionale Programmierung dabei helfen kann, qualitativ hochwertigen Quellcode zu schreiben. Dabei lege ich besonderen Wert auf Lesbarkeit, Testbarkeit und Ausdrucksstärke. Ich gehe bei der Entwicklung von APIs gern explorativ vor. Es ist wie beim Malen eines Bildes, der Künstler macht einen Strich und lässt das Gesamte auf sich wirken. Seine Intuition bzw. Erfahrung sagt ihm, wo er den nächsten Strich platzieren muss, denn das Bild ist von vornherein da – es wird lediglich sichtbar gemacht.

Sowohl in der funktionalen Programmierung als auch in der objektorientierten Programmierung wird zwischen Daten und Operation auf Daten unterschieden. Eine Funktion hat Eingabe- und Ausgabedaten. Dabei kann eine Funktion selbst auch Eingabe- oder Ausgabedatum sein. Eine solche Funktion nennen wir Funktion höherer Ordnung.

<T> void async(Supplier<T> call, Consumer<T> callback);

// Anwendungsbeispiel
async(() -> computeValue(), value -> { … });

Die beschriebene Funktion async ist eine Funktion höherer Ordnung, da sie zwei Eingabefunktionen besitzt. Die Grenzen generischer Typparameter haben wir hierbei zunächst vernachlässigt. Ein hilfreicher Aspekt der funktionalen Programmierung ist, dass die Signatur einer Methode bereits Hinweise auf deren Bedeutung gibt, denn eine Funktion wandelt gewisse Eingaben in genau eine Ausgabe um. Gibt eine Funktion nichts zurück, ist das ein Indiz dafür, dass die äußere Welt durch Seiteneffekte verändert wird, da die Funktion ansonsten bedeutungslos wäre. Unser Ziel sollte es sein, die äußere Welt nur dann zu verändern, wenn es notwendig ist. Ganz vermeiden können wir es nicht, da ein Programm ohne Seiteneffekte quasi nichts tun würde, außer Rechenkapazität in Anspruch zu nehmen. Es ist hilfreich, Seiteneffekte nicht hart zu kodieren, sondern sie als variables Verhalten in Form von Funktionen in andere Funktionen zu injizieren. In unserem Beispiel tun wir dies mit Hilfe der callback-Funktion.

Den vollständigen Artikel lesen Sie in der Ausgabe:

Java Magazin 3.18 - "20 Jahre Java Magazin"

Alle Infos zum Heft
579827743Objektfunktionale Programmierung mit Vavr
X
- Gib Deinen Standort ein -
- or -