Monadentransformer — Bausteine, die eine Monade um die Fähigkeiten einer anderen erweitern, ohne beide von Hand zu verschmelzen. Sie lösen das Problem, dass eine einzelne Monade nur einen Kontext trägt: Mit Transformatoren werden zum Beispiel Fehlerbehandlung, Zustand und Ein-/Ausgabe in einem einzigen Typ kombiniert.
Das Problem: nur ein Kontext pro Monade
Die Maybe-Monade kann Werte, die fehlen können. Die IO-Monade kann Seiteneffekte. Aber was, wenn eine Datei gelesen werden soll, die möglicherweise nicht existiert? Beide Eigenschaften sind nötig — doch Maybe IO a und IO (Maybe a) verhalten sich unterschiedlich und sind unpraktisch zu schreiben.
Ein Monadentransformer ist ein Wrapper-Typ, der eine innere Monade um eine zusätzliche Fähigkeit erweitert. MaybeT IO a ist ein Transformer, der die IO-Monade um Maybe-Fähigkeiten erweitert: Die Berechnung läuft in IO, kann aber jederzeit mit Nothing abbrechen.
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Die Standardsammlung
Fast jede gängige Monade hat eine Transformer-Variante:
MaybeT— Optionalität,EitherT(bzw.ExceptT) — Fehler als WerteStateT— Zustand (State-Monade)ReaderT— schreibgeschützte Umgebung (Reader-Monade)WriterT— akkumulierte Ausgabe (Writer-Monade)ContT— Fortsetzungen
Ein Transformer nimmt die innere Monade als Parameter: StateT s (ReaderT r IO) a ist ein Stack aus IO, Umgebung und Zustand. Die Stapelreihenfolge entscheidet über die Semantik — StateT s Maybe und MaybeT (State s) verhalten sich bei Fehlern unterschiedlich, weil ein abgebrochener Zustand entweder verloren geht oder erhalten bleibt.
Lift und liftIO: durch den Stack reisen
Die Funktion lift :: m a -> t m a (Klasse MonadTrans) hebt eine Aktion der inneren Monade um eine Ebene in den Stack. Für IO gibt es die bequeme Abkürzung liftIO (Klasse MonadIO): Sie hebt eine IO-Aktion direkt in einen beliebig tiefen Stack, der auf IO aufbaut — unabhängig davon, wie viele Transformatoren dazwischenliegen.
Ein bewährtes Muster ist der ReaderT-Stack: ReaderT r IO als Grundgerüst einer Anwendung mit Konfiguration r, in dem Zustand, Fehlerbehandlung und Logging als weitere Schichten hinzukommen. Die Klasse MonadReader/MonadState erlaubt es, auf die jeweilige Fähigkeit zuzugreifen, ohne die genaue Stack-Tiefe zu kennen.
Monadentransformer sind der praktische Grund, warum die Monade in Haskell-Programmen allgegenwärtig ist: Sie machen kombinierte Effekte typsicher und bleiben dabei rein — ohne Ausnahmen und globale Variablen. Verwandte Grundlagen: Funktionale Programmierung, Typsystem, Haskell-Befehle.