Begriff
Functor
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Ein Functor ist einfach ein Container, über den man map aufrufen kann.
Eine Kiste, in der was drin ist.
Du hast eine Funktion plusEins(x).
Du willst sie auf den Inhalt der Kiste anwenden, ohne die Kiste auszupacken.
Beispiel 1: Liste [1, 2, 3]. -> map(plusEins) -> [2, 3, 4].
Beispiel 2: Optional (Maybe) Just(5). -> map(plusEins) -> Just(6).
Beispiel 3: Promise Future(10). -> map(plusEins) -> Future(11).
Alle diese Dinger (Liste, Optional, Promise) sind Functoren.
Merksatz: Ein Datentyp (Container), der eine map-Funktion bereitstellt, um eine Funktion auf den darin enthaltenen Wert anzuwenden, ohne die Struktur des Containers zu ändern.
In JavaScript: Array.map.
In Java: Optional.map oder Stream.map.
Es erlaubt dir, Daten zu transformieren, ohne dich um "Ist die Liste leer?" oder "Ist der Wert null?" zu kümmern. Der Functor erledigt das Safety-Check-Handling für dich.
Wenn die Kiste leer ist (Nothing / []), macht map einfach nichts (gibt leere Kiste zurück). Kein Absturz.
1. Functor Laws
Damit es mathematisch sauber ist, müssen zwei Regeln gelten:
- Identity:
map(x => x)darf nichts ändern. - Composition:
map(x => f(g(x)))muss das Gleiche sein wiemap(g).map(f). Das erlaubt Compilern (wie in Haskell), Code zu optimieren ("Fusioning": zwei Schleifen zu einer verschmelzen).
1. Kovariante und Kontravariante Functors
Programmiersprachen verlangen mathematische Präzision.
Ein Kovarianter Functor (der normale map Fall) behält die Richtung von Typumwandlungen bei ($A \rightarrow B$ bedeutet $F(A) \rightarrow F(B)$).
Ein Kontravarianter Functor (contramap) dreht die Richtung um. Wenn du eine Funktion hast, die ein $B$ in ein $C$ verwandelt, kannst du sie nicht auf eine "Kiste" anwenden, die ein $B$ erwartet (wie ein Predicate / Vergleicher). contramap erlaubt dir, die Kiste so zu modifizieren, dass sie jetzt statt eines $B$ ein $A$ frisst, indem das $A$ intern erst zu $B$ umgeformt wird. Das wird intensiv in asynchronem Routing und Decoder-Libraries (z.B. io-ts) verwendet.
2. Endofunctors in Software
Das berühmte Zitat: "Eine Monade ist nur ein Monoid in der Kategorie der Endofunktoren".
Ein Endofunctor ist ein Functor, der von einer Kategorie in dieselbe Kategorie abbildet.
In der Programmierung bewegen wir uns eigentlich immerschon in der strikten Kategorie der Typen (Satz der Scala/Haskell-Typen). Ein Array-Functor nimmt einen Scala-Typ (Int) und gibt einen Scala-Typ zurück (Array[Int]). Weil wir die Domäne (Scala-System) nicht verlassen (wir mappen nicht plötzlich zu SQL-Datentypen), sind alle Programmier-Funktoren mathematisch Endofunktoren.
3. Functor Composition und Traversals
Was passiert, wenn du Kisten in Kisten hast? Optional<List<String>>.
Willst du den String lesen/ändern, müsstest du opt.map(list => list.map(s => f(s))) schreiben.
Da Functoren mathematisch "komponierbar" sind, ist der Compose-Functor zweier Functoren automatisch wieder ein Functor. In puren funktionalen Sprachen baut man sich abstrakte Typen (Compose F G a), die direkt ein abgeflachtes map anbieten. Dadurch entfällt das Pyramid-of-Doom Pattern beim Mappen verschachtelter Struktur-Container.
Quick-Check
Ist ein Set ein Functor?
In der Theorie ja. In der Praxis oft schwer, weilmapden Typ ändern kann, aber ein Set oft sortiert sein muss (was bei Typänderung kaputt gehen kann).Unterschied zu Monad?
Jede Monade ist ein Functor. Aber nicht jeder Functor ist eine Monade. Functor kann nurmap(einfache Funktion). Monad kannflatMap(Funktion, die wieder eine Kiste zurückgibt).Klingt abstrakt?
Ist es. Category Theory ist die abstrakteste Mathe überhaupt. Aber in der Praxis heißt es einfach: "Das Ding hat eine .map() Methode."