Ein Funktor ist ein Entwurfsmuster der funktionalen Programmierung, das es erlaubt, eine Funktion auf Werte innerhalb eines generischen Typs anzuwenden, ohne die Struktur dieses Typs zu verändern. Der Begriff stammt aus der Kategorientheorie, wo ein Funktor eine strukturerhaltende Abbildung zwischen zwei Kategorien ist.
Die Grundidee
Ein Funktor besteht aus einem Typkonstruktor f und einer Abbildung fmap mit der Signatur
fmap :: (a -> b) -> f a -> f b
fmap nimmt eine gewöhnliche Funktion a -> b und hebt sie in den Kontext f an: Aus einem f a wird ein f b. Der Kontext bleibt dabei unverändert — bei einer Liste ändert sich die Länge nicht, bei einem Maybe bleibt es Nothing oder Just, bei Either bleibt es Left oder Right.
Die Funktor-Gesetze
Damit ein Typ ein gültiger Funktor ist, müssen zwei Gesetze gelten:
- Identity:
fmap id = id— die Identitätsfunktion verändert nichts. - Composition:
fmap (f . g) = fmap f . fmap g— das Hintereinanderausführen von Funktionen lässt sich mit dem Anwenden vertauschen.
Diese Gesetze garantieren, dass fmap die Struktur wirklich nicht antastet und sich wie eine echte strukturerhaltende Abbildung verhält.
Beispiele und Pendants
In Haskell ist fmap als Infix-Operator <$> verfügbar. Die Listen-Monade ist ein Funktor, dessen fmap dem bekannten map entspricht. Auch Maybe, Either und IO sind Funktoren. In anderen Sprachen findet man dieselbe Idee unter anderen Namen: map bei Rust-Option und Scala, Array.map in JavaScript, Option.map in F#.
Der Funktor ist die schwächste Stufe einer Hierarchie: Jeder Applicative-Functor ist ein Funktor, und jede Monade ist ein Applicative-Functor. Wer die Stufen versteht, findet sich in der funktionalen Programmierung sicher zurecht.
Abgrenzung
Ein Funktor kann nur Funktionen mit einem Argument anwenden. Für Funktionen mit mehreren Argumenten braucht man einen Applicative-Functor, für Berechnungen mit abhängigem Ablauf eine Monade.
Verwandte Grundlagen: Funktionale Programmierung, Lambda-Kalkül, Typsystem, Higher-Order-Funktionen, Haskell-Befehle.