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, wobei runReaderT die 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.