Reader-Monade kapselt eine schreibgeschützte Umgebung in der funktionalen Programmierung. Viele Funktionen brauchen dieselbe Konfiguration: Datenbank-Adresse, Port, App-Einstellungen. Statt diese Werte durch jede Funktionssignatur zu reichen, stellt die Reader-Monade die Umgebung implizit bereit.
Die Idee
Eine Reader-Berechnung ist im Kern nur eine Funktion von der Umgebung r auf das Ergebnis a:
newtype Reader r a = Reader { runReader :: r -> a }
ask :: Reader r r -- ganze Umgebung lesen
asks :: (r -> a) -> Reader r a -- Projektion (Feld lesen)
local :: (r -> r) -> Reader r a -> Reader r a
Ein Beispiel liest einen Konfigurationswert:
data Config = Config { serverName :: String, port :: Int }
gruss :: Reader Config String
gruss = do
name <- asks serverName
pure ("Server: " ++ name)
runReader gruss (Config "db1" 5432) -- "Server: db1"
ask liefert die komplette Umgebung, asks einen ausgewählten Teil (hier das Feld serverName), local modifiziert die Umgebung nur für eine Teilberechnung.
Praxis
- Die Reader-Monade ist reine Dependency Injection: Konfiguration wird zentral bereitgestellt, Funktionen bleiben ohne globale Variablen testbar.
- Der Standard-Stack vieler Haskell-Applikationen ist
ReaderT r IO— Umgebung plus Seiteneffekte, wobeirunReaderTdie Umgebung beim Start injiziert. - Abgrenzung: Die State-Monade kann zusätzlich schreiben, die IO-Monade kapselt Seiteneffekte; die Reader-Monade selbst ist rein lesend und referenziell transparent.
Verwandte Grundlagen: Monade, Funktionale Programmierung, Typsystem, Haskell-Befehle.