Funktionskomposition (englisch function composition) verbindet zwei oder mehr Funktionen zu einer neuen Funktion: Das Ergebnis der einen Funktion wird zur Eingabe der nächsten. In mathematischer Schreibweise ist (f ∘ g)(x) = f(g(x)) — zuerst wird g auf x angewendet, dann f auf das Ergebnis.

Von der Mathematik in die Programmierung

Die Verkettung von Funktionen ist ein klassisches Konzept der Mathematik und wurde von der funktionalen Programmierung übernommen. Voraussetzung ist, dass Funktionen als Werte behandelbar sind — das leisten erstklassige Funktionen. Komposition ist eine typische Higher-Order-Funktion: Sie nimmt Funktionen als Argumente und gibt eine neue Funktion zurück.

pipe und compose

Die Reihenfolge entscheidet: compose(f, g) wendet von rechts nach links an (zuerst g, dann f), pipe(f, g) von links nach rechts (zuerst f, dann g). Viele Bibliotheken stellen beide Hilfsfunktionen bereit, zum Beispiel Lodash (_.flow) oder Ramda.

const trim = s => s.trim();
const toLowerCase = s => s.toLowerCase();
const splitWords = s => s.split(/s+/);

// pipe: links nach rechts lesbar
const normalize = pipe(trim, toLowerCase, splitWords);
normalize("  Hallo  WELT "); // ["hallo", "welt"]

Warum komponieren?

  • Lesbarkeit: Eine Kette benannter Schritte ist oft klarer als verschachtelte Aufrufe wie splitWords(toLowerCase(trim(s))).
  • Wiederverwendung: Kleine, reine Funktionen lassen sich in beliebigen Kombinationen wiederverwenden.
  • Testbarkeit: Jede Stufe der Kette ist einzeln testbar.

Zusammen mit Currying — das eine Argument nach dem anderen übernimmt — entstehen besonders elegante Kompositionen: map(square) ist bereits eine fertige Funktion, die in eine Pipeline eingehängt werden kann. Die Praxis zeigt sich auch in Unix-Pipelines, wo Programme über Standardein- und -ausgabe verkettet werden.

Verwandte Grundlagen: Higher-Order-Funktion, Currying, Reine Funktionen.