Monaden

However, one of the greatest barriers for learning it (Haskell) are monads. Although they are quite simple, they can be very confusing for a beginner. (Ertugrul Söylemez)

Grundproblem

  • Haskell ist rein funktional
    • Programme bestehen aus mathematischen Funktionen,die nur von ihren Argumenten abhängen right referentielle Transparenz
    • da die Ergebnisse der Funktionsaufrufe nur von den Eingaben abhängen, wird der Programmfluß durch Datenabhängigkeiten vorgegeben
    • in diesem System kann es keinen Zufallszahlen, keine Ein- und Ausgabe, keinen Zustand geben
  • Lösung
    • führe für alle Arten von Seiteneffekten eigene Subsysteme ein, die von der rein funktionalen Kernsprache Haskell getrennt sind
    • kapsele die Typen der Kernsprache in den Subsystemen
    • durch return wird ein Typ ins Subsystem eingebunden und durch >>= (bind) wird das sequentielle Ausführen von Operationen im Subsystem definiert

Verwandte

  • Exceptions Spezifikationen in Methodenaufrufen (Java,C++)
  • C++

Beispiel: Abfrage eines Abstammungsbaum

Person

  • besitzt einen Namen, einen Vater und eine Mutter, falls diese bekann sind
    data Person = Person {name::String,father::Maybe Person,mother::Maybe Person}   
  • sind darstellbar
    instance Show Person where
    show s = show (name s)

Abfrage

  • Ahnen sind nicht immer bekannt right Ergebnistyp muß zwischen, es gibt ein Ergebnis und es gibt kein Ergebnis, unterscheiden
  • verwende als Ergebnistyp die Maybe Monade, mit
    • Nothing: Wert kann nicht gefunden werden
    • Just a:Ergebnis a ist in der Maybe Monade gekapselt
      Prelude> :info Maybe
      data Maybe a = Nothing | Just a -- Defined in Data.Maybe
      ...
    • Nothing: Wert kann nicht gefunden werden
    • Just a: Ergebnis a in der Monade Maybe gekapselt

Die Mann's

golo = Person "Golo Mann" (Just thomas) (Just katja)

thomas= Person "Thomas Mann" (Just heinrich) (Just julia)
katja= Person "Katja Pringsheim" (Just alfred) (Just hedwig)

heinrich= Person "Thomas Johann Heinrich Mann" (Just johann) (Just elisabeth)
julia= Person "Julia da Silva-Bruhns" Nothing (Just maria)
alfred= Person "Prof. Dr. phil. Alfred Pringsheim" Nothing Nothing
hedwig= Person "Hedwig Anna Dohm" Nothing Nothing

johann= Person "Johann Siegmund Mann" Nothing Nothing
elisabeth= Person "Elisabeth Marty" Nothing Nothing
maria= Person "Maria da Silva" Nothing Nothing

Konkrete Abfragen

  • Vater der Mutter:
    maternalGrandfather :: Person -> Maybe Person
    maternalGrandfather p =
    case mother p of
    Nothing -> Nothing
    Just m -> father m
  • beide Großväter
    bothGrandfathers :: Person -> Maybe (Person, Person)
    bothGrandfathers p =
    case father p of
    Nothing -> Nothing
    Just f ->
    case father f of
    Nothing -> Nothing
    Just gf ->
    case mother p of
    Nothing -> Nothing
    Just m ->
    case father m of
    Nothing -> Nothing
    Just gm ->
    Just (gf, gm)
  • beide Abfragen verwenden nur, das zwei verschieden Ergebnistypen (Nothing und Just a) aus der Anfrage resultieren können right hässlicher Code
  • das Ergebnis der Funktion ist die letzte case Anweisung rightNothing oder Just (gf, gm)
    *Main> father golo
    Just "Thomas Mann"
    *Main> mother golo
    Just "Katja Pringsheim"
    *Main> maternalGrandfather golo
    Just "Prof. Dr. phil. Alfred Pringsheim"
    *Main> maternalGrandfather thomas
    Nothing
    *Main> bothGrandfathers golo
    Just ("Thomas Johann Heinrich Mann","Prof. Dr. phil. Alfred Pringsheim")
    *Main> bothGrandfathers thomas
    Nothing
    *Main>
  • nütze nun aus, das auf Monaden das sequentielle Abarbeiten von Befehlen per Definition möglich ist
    bothGrandfathersBindNotation p =
    father p >>=
    (\f -> father f >>=
    (\gf -> mother p >>=
    (\m -> father m >>=
    (\gm -> return (gf,gm) ))))
  • falls eine Operation Nothing als Ergebnis zurückgibt, ist das Ergebnis der Gesamtoperation durch Nothing schon vorgegben
  • der Umgang mit den verschieden Rückgabetypen wird durch die bind Operation (>>=) implizit gehandelt right reduce boilerplate code
  • der bind Operator (>>=)verknüpft den linken Aufruf mit dem rechten, indem es die linke monadische Aktion ausführt und das Ergebnis an die rechte Lambda-Funktion bindet
    • father p >>= (\f -> father f ... : ermittle den Vater von p, und binde diesen an f, um dessen Vater wiederum zu ermitteln
  • dies ist aber noch nicht wirklich schön, dazu gibt es syntactic sugar in Form der do notation
    parents p = do
    f <- father p
    m <- mother p
    return (f,m)

    bothGrandfathersDoNotation p = do
    f <- father p
    gf <- father f
    m <- mother p
    gm <- father m
    return (gf,gm)
  • der Anwendungscode bleibt der gleiche
    *Main> parents golo
    Just ("Thomas Mann","Katja Pringsheim")
    *Main> bothGrandfathersBindNotation golo
    Just ("Thomas Johann Heinrich Mann","Prof. Dr. phil. Alfred Pringsheim")
    *Main> bothGrandfathersDoNotation golo
    Just ("Thomas Johann Heinrich Mann","Prof. Dr. phil. Alfred Pringsheim")

Die Maybe Monade

  • Klassischer Anwendungsfall:
    • Datenbank- oder Dictionaryabfragen
data Maybe a = Nothing | Just a

instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
(Just x) >>= f = f x

  • data Maybe a
    • ist ein Typkonstruktur, der durch den Typ a parametrisiert wird
    • besitzt zwei Datenkonstruktoren Nothing und Just a (Datenkonstruktoren entsprechen den gewohnten Konstruktoren aus C++ und Java)
  • instance Monad Maybe where
    • Maybe ist Instanz der Typklasse Monade und muß daher die entsprechenden Methoden implementieren
    • return führt einen reinen Typ in die Typklasse Maybe ein
    • >>=erklärt die Verknüpfung von Operationen
      1. Nothing >>= f = Nothing: propagiert das Fehlen eines Ergebnisses weiter
      2. Just x >>= f= f x: ergibt der letzte Aufuf einen monadischen Wert Just x, wird der reine Wert x ausgepackt und die Funktion f darauf angewendet
  • Maybe Monade
    • der Typkonstruktor kapselt einen Typ a
    • führt einen Werte mittels return in die Maybe Monade ein
    • erklärt mittels >>= (bind) das Verknüpfen von monadischen Werten
Einmal in der Monade gefangen, bleibt der Wert immer in der Monade gebunden.

Definition

A monad is a wrapper type around another type (the inner type), which adds a certain structure to the inner type and allows you to combine computations of the inner type in a certain way.( Ertugrul Söylemez )
  • ein bisschen formaler
    -- wrapper type m around a inner type a
    data m a = ...

    -- return is a wrapper type around the inner type
    return :: a -> m a

    -- bind or (>>=) combines operations of the inner type
    (>>=) :: m a -> (a -> m b) -> m b
  • Monaden beschreiben mathematische Strukturen ähnlich zu Gruppen (Kategorientheorie)

Vorteile

  • Trennung
    • der rein funktionale Code wird vom unreinen Code separiert right Mächtigkeit des rein funktionalen Codes bleibt erhalten.
  • Modularisierung
    • Seiteneffektbehafterter Code läuft in einem vollkommen autonomen Subsystem
    • Monaden repräsentieren verschieden Aspekte von Seiteneffekten
  • Abstraktion
    • häufig wiederkehrende Muster ( >>= und >> ) werden in Methodendefinitionen gekapselt und können so implizit beim Arbeiten mit Monaden verwendet werden

Theorie

Methoden

  • eine Monade ist eine Typklasse, die vier Methoden besitzt
    Prelude> :info Monad
    class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    (>>) :: m a -> m b -> m b
    return :: a -> m a
    fail :: String -> m a
  • >>= (bind) verknüpft Operationen in der Monade, sodass das Ergebnis der ersten Operation (Producer) an die zweite Operation (Consumer) gebunden wird
  • >>(then) führt zwei Operationen nacheinander aus, indem das Ergebnis der ersten Operation ignoriert wird
    • die Standardimplementierung von >> ist : (>>) :: m a -> (\_ -> m b) -> m b
  • return kapselt einen reinen Wert in der Monade
  • fail Umgang mit Fehlerbehandlungen in der Monade (Pattern Matching in der do Notation)
  • sowohl fail als auch >> besitzen Standardimplementierung, sodass jede Instanz einer Monade nur bind und return definieren muß
  • Beispiel IO Monade:
    • Interaktiv in der Haskell Shell
      Prelude> :type putStr
      putStr :: String -> IO ()
      Prelude> :type putStrLn
      putStrLn :: String -> IO ()
      Prelude> putStr "Only" >> putStr " for " >> putStrLn "testing."

      Only for testing.
      Prelude> :type getLine
      getLine :: IO String
      Prelude> getLine >>= putStr
      Get a line.
      Get a line.Prelude>
    • do Notation als Skript monad.hs
      thenAction= do
      putStr "Only"
      putStr " for "
      putStrLn "testing."

      bindAction= do
      line <- getLine
      putStr line

Axiome

bind und returneiner Monade müssen folgenden Gesetzen genügen.
  1. return x >>= f == f x
    • return ist linksneutral
  2. c >>= return == c
    • return ist rechtsneutral
  3. c >>= (\x -> f x >>= g) = (c >>f) >>= g
    • bind ist assoziativ
  • Beispiel zur IO Monade und List Monade:
    Prelude> let test= "Only for testing."
    Prelude> return test >>= \x-> putStrLn x
    Only for testing.
    Prelude> putStrLn test
    Only for testing.
    Prelude> getLine >>= return
    Only for testing.

    "Only for testing."
    Prelude> return "Only for testing."
    "Only for testing."
    Prelude> [1..10] >>= \x -> [x*x] >>= \y -> return (y+10)

    [11,14,19,26,35,46,59,74,91,110]
    Prelude> [1..10] >>= (\x -> [x*x] >>= \y -> return (y+10))
    [11,14,19,26,35,46,59,74,91,110]

Abgeschlossenheit

  • Monaden sind von der Aussenwelt abgeschlossene Subsysteme
  • die Methoden wie return, bind und then sind Verknüpfungen in der Monade
  • Monaden müssen keine Funktionen anbieten, um die Werte aus ihr zu extrahieren
  • abhängig von der Monade gibt es jedoch Funktionen, um einen Wert aus ihr zu extrahieren
    • List Monade: head oder last
    • Maybe Monade: fromJust oder fromMaybe
      Prelude> head [1..10]
      1
      Prelude> last [1..10]
      10
      Prelude> :module Maybe
      Prelude Maybe> Just 3
      Just 3
      Prelude Maybe> fromJust ( Just 3 )
      3
      Prelude Maybe> lookup 3 [(1,'A'),(2,'B'),(3,'C')]
      Just 'C'

      Prelude Maybe> fromJust ( lookup 3 [(1,'A'),(2,'B'),(3,'C')] )
      'C'
      Prelude Maybe> fromJust ( lookup 1000 [(1,'A'),(2,'B'),(3,'C')] )

      *** Exception: Maybe.fromJust: Nothing
      Prelude Maybe> fromMaybe 'X' ( lookup 1 [(1,'A'),(2,'B'),(3,'C')] )
      'A'
      Prelude Maybe> fromMaybe 'X' ( lookup 1000 [(1,'A'),(2,'B'),(3,'C')] )

      'X'

Bekannte Monaden

IO

  • Problem:
    • Programm müssen mit der Aussenwelt kommunizieren ( Ein-, Ausgabe, Dateizugriffe, ... )
  • Lösung:
    • IO Monade bietet die Schnittstelle zu Aussenwelt an right die IO Monade besizt implizit den Zustand der Welt
    • das Hauptprogramm besitzt in Haskell die Deklaration: main :: IO () right dies ist eine Aktion, die mit der Aussenwelt kommuniziert und nichts ( "()" ) zurückgibt
    • die main Aktion wird beim Starten des Programms ausgeführt
    • aus der IO Monade können keine Werte extrahiert werden
  • Hello worldin Haskell
    main :: IO ()
    main = putStrLn "Hello world!"

Maybe

List

  • Problem:
    • eine Berechnung kann beliebige viele Werte zurückgeben
  • Lösung:
    • die List Monade kann beliebig viele Werte eines Types annehmen
  • klassischer Anwendungsfall
    • Berechnungen über Sequenzen unbekannter Länge
      instance Monad [] where
      xs >>= f = concat . map f xs
      return x = [x]
      fail s = []
  • bei Liste gibt es viel syntatic sugarum den Umgang mit ihnen zu vereinfachen
    • statt [] a wird bei ihnen [a] geschrieben
    • viele Varianten, Listen zu erzeugen
      1. klassische Monaden Notation
        Prelude> [1,2,3] >>= \a -> [10,100,1000] >>= \b -> return (a^2+b^2)

        [101,10001,1000001,104,10004,1000004,109,10009,1000009]
      2. do Notation
        generateList= do
        a <- [1,2,3]
        b <- [10,100,100]
        return (a^2 + b^2)
      3. List comprehension
        Prelude> [ a^2+b^2 | a <- [1,2,3], b <- [10,100,1000] ]
        [101,10001,1000001,104,10004,1000004,109,10009,1000009]

State

  • Problem:
    • ein Programm muß während seiner Ausführung einen modifizierbaren Zustand aufbauen, auf den es zugreifen kann. right globale Variablen in imperativen Programmiersprachen
  • Lösung:
    • erkläre eine Monade, die den Zugangsübergang von einem alten in einen neuen Zustand beschreibt
  • klassischer Anwendungsfall:
    • Berechnung über Sequenzen von Operationen, die einen gemeinsamen Zustand benötigen
      newtype State s a = State { runState :: (s -> (a,s)) } 

      instance Monad (State s) where
      return a = State ( \s -> (a,s) )
      (State x) >>= f = State ( \s -> let (v,s') = x s in runState (f v) s' )
    • runState führt den Zugangsübergang aus
    • durch die Verknüpfung ( >>= ) zweier Zustände, wird ein neuer Wert (f v) und ein neuer Zustand s' produziert
    • die Funktionen get und putermöglichen es, den aktuellen Zustand zu extrahieren bzw. einen neuen Zustand zu setzen
      get :: State s s
      put :: s -> State s ()
  • Beispiele
    • get und put in Aktion
      • stateTest liest mit der Funktion get den aktuellen Zustand ein, schreibt den neuen Zustand "newState" und gibt als Ergebnis den initialen Zustand in Grossbuchstaben zurück
        import Data.Char
        import Control.Monad.State
        stateTest = do
        initialState <- get
        put ( "newState" )
        return ( map(\x -> toUpper x) initialState )
      • durch runState wird die Aktion ausgeführt und das Paar (Ergebnis,neuer Zustand) ausgegeben
        *Main Data.Char> runState stateTest "initialState"
        ("INITIALSTATE","newState")
    • Zufallswerte
      • ein klassischer Anwendungsfall für die State Monade ist das Erzeugen von Zufallswerten
        import Data.Word
        import Control.Monad.State
        type LCGState = Word32

        lcg :: LCGState -> (Integer, LCGState)
        lcg s0 = (output, s1)
        where s1 = 1103515245 * s0 + 12345
        output = fromIntegral s1 * 2^16 `div` 2^32

        getRandom :: State LCGState Integer
        getRandom = do
        s0 <- get
        let (x,s1) = lcg s0
        put s1
        return x

        addThreeRandoms :: State LCGState Integer
        addThreeRandoms = do
        a <- getRandom
        b <- getRandom
        c <- getRandom
        return (a+b+c)
      1. lcg
        • der linear congruential generator erzeugt zu einem Zustand s0 eine Paar Zufallszahl und neuer Zustand (output,s1)
          *Main> lcg 0
          (0,12345)
          *Main> lcg 1
          (16838,1103527590)
      2. getRandom
        • kapselt das Berechnen der Zufallszahl in einer Funktion
        • so <- get : extrahiert den aktuellen Zustand
        • let (x,s1) = lcg s0 : führt die Zufallsfunktion aus und bindet die Zufallszahl und den neuen Status an das Paar (x,s1)
        • put s1 : setzt den neuen Zustand s1
        • return x : gibt die Zufallszahl zurück
        • mit runState, evalState und execStatelassen sich die Ergebnisse der getRandom Funktion anfordern
          *Main> runState getRandom 0
          (0,12345)
          *Main> evalState getRandom 0
          0
          *Main> execState getRandom 0
          12345
      3. addThreeRandoms
        • durch das Kapseln der Zustandsübergänge in getRandom ist es einfach möglich drei Zufallszahlen anzufordern und zu addieren
        • das Erzeugen der Zufallszahlen findet vollständig implizit statt
          *Main> runState addThreeRandoms 0
          (96992,2802067423)
          *Main> evalState addThreeRandoms 0
          96992
          *Main> execState addThreeRandoms 0
          2802067423
          *Main> runState addThreeRandoms 0
          (96992,2802067423)
          *Main> runState addThreeRandoms 1
          (65477,662824084)
        • in addThreeRandom werden 3 verschiedene Zufallszahlen erzeugt, da jede getRandom Funktion von einem anderen Zustand ausgeht

Weiterführende Konzepte

MonadPlus

Viele Monaden abstrahieren über Berechnungen.
  • Problem:
    • Berechnungen liefern nicht immer Ergebnisse und erfordern daher eine weitere Berechnung
  • Lösung:
    • Instanzen der speziellen Monade MonadPlus definieren Methoden mzero und mplusfür das Fehlen eines Ergebnisses einer Berechnung und die bedingte Berechnung eines neuen Wertes
      Prelude> :module +Control.Monad
      Prelude Control.Monad> :info MonadPlus
      class (Monad m) => MonadPlus m where
      mzero :: m a
      mplus :: m a -> m a -> m a
      -- Defined in Control.Monad
      instance MonadPlus [] -- Defined in Control.Monad
      instance MonadPlus Maybe -- Defined in Control.Monad
  • die zwei bekannten Monaden [] und Maybe sind Instanzen der Monade MonadPlus
  • die Implementerierung der zwei Funktionen ist sehr naheliegend
    instance MonadPlus Maybe where
    mzero = Nothing

    Nothing `mplus` y = y
    x `mplus` y = x

    instance MonadPlus [] where
    mzero = []
    mplus = (++)
  • ein paar Beispiele
    Prelude Control.Monad> mplus (Just 3) (Just 4)
    Just 3
    Prelude Control.Monad> mplus Nothing (Just 4)
    Just 4
    Prelude Control.Monad> mplus Nothing Nothing
    Nothing
    Prelude Control.Monad> Just 3 `mplus` Just 4
    Just 3
    Prelude Control.Monad> mplus (Just 3) (Just 4) == Just 3 `mplus` Just 4
    True
    Prelude Control.Monad> [1..10] `mplus` [2..20]

    [1,2,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
    Prelude Control.Monad> [] `mplus` [2..20]
    [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
    Prelude Control.Monad> [] `mplus` []
    []
  • eine Funktion lässt sich in Haskell als Operator verwenden, wenn diese Funktion mit ` `gewrappt wird
    • daher ist mplus (Just 3) (Just 4) == Just 3 `mplus` Just 4

Funktionen für Monaden

Rund um Monaden gibt es viele Funktionen, die das Programmieren mit Monaden vereinfachen. Diese Funkion bieten oft imperative Kontrollstrukturen in den Monaden ein. Teil der Funktionalität muß explizit über das Modul Control.Monad geladen werden.
Viele dieser Funktionen existieren in zwei Geschmacksrichtungen - mit und ohne abschliessendem Unterstrich. Funktionen mit abschliessendem Unterstrich ignorieren das Ergebnis. Die Namen der monadischen Funktionen lehnen sich gerne an ihre reine Pendants ab, enthalten aber ein abschliessendes grosses M.

Bilde eine monadische Funktion auf eine Liste ab

mapM  :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

forM :: Monad m => [a] -> (a -> m b) -> m [b]
forM_ :: Monad m => [a] -> (a -> m b) -> m ()

  • der Unterschied zwischen forM und mapM ist lediglich, das die zwei Argumente vertauscht werden
  • Beispiel:
    Prelude> mapM_ putStr ["Only ","for ","testing. \n"]
    Only for testing.
    Prelude> map (\x -> x*x) [10,20,30]

    [100,400,900]
    Prelude> mapM (\x -> [x*x]) [10,20,30]
    [[100,400,900]]
    Prelude Control.Monad> forM [10,20,30] (\x -> [x*x])
    [[100,400,900]]
    Prelude> :type (\x -> x*x)
    (\x -> x*x) :: (Num a) => a -> a
    Prelude> :type (\x -> [x*x])
    (\x -> [x*x]) :: (Num a) => a -> [a]

Führe eine Liste von Berechnung nacheinander aus

 
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()

  • Beispiel
    Prelude System.Environment> sequence_ [putStr "Only ",putStr "for ",putStr "testing. \n"]
    Only for testing.
    Prelude System.Environment> sequence [getProgName, getEnv "HOME", getLine] >>= print
    getline need's something from the commandline.

    ["<interactive>","/home/grimm","getline need's something from the commandline."]
    Prelude System.Environment>
  • die entsprechende monadisch definiert und ausgeführt ist deutliche umfangreicher
    import System.Environment

    main :: IO ()
    main = do
    results <- do
    a <- getProgName
    b <- getEnv "HOME"

    c <- getLine
    return [a,b,c]

    print results

Führe eine Berechnung (un)endlich oft aus

forever :: Monad m => m a -> m b
replicateM :: Monad m => Int -> m a -> m [a]
replicateM_ :: Monad m => Int -> m a -> m ()

  • Beispiel:
    Prelude Control.Monad> replicateM 5 "2"
    ["22222"]
    Prelude Control.Monad> replicateM 5 [2]
    [[2,2,2,2,2]]

Bedingtes Ausführen von Berechnungen

when   :: Monad m => Bool -> m () -> m ()
unless :: Monad m => Bool -> m () -> m ()

  • Beispiel
    import System.Exit

    main :: IO ()
    main =
    forever $ do
    line <- getLine
    when (line == "quit") exitSuccess
    putStrLn line

Wende eine nicht-monadische Funktion in einer Monade an

liftM :: Monad m => (a -> b) -> m a -> m b
Diese Technik wird gerne als liftingbezeichnet, da eine reine Funktion in die Monade gehoben und angewandet wird. Für Listen (M == []) ist liftM die bekannte Funkton map.
  • Beispiel:
    Prelude Control.Monad> sin 0
    0.0
    Prelude Control.Monad> liftM sin (Just 0)
    Just 0.0
    Prelude Control.Monad> liftM sin (Nothing)
    Nothing

Monaden Transformer

  • Problem:
    • Monaden sind in sich abgeschlossen. Mit den bisherigen Techniken war es bisher nicht möglich, Monaden zu kombinieren.
  • Lösung:
    • Ein Monadtransformer ist eine Monade, die über eine Monade parametrisiert wird. So lässt sich in einer State Monade mit der Aussenwelt kommunizieren.
Die Monaden, die mit einer zusätzlichen Monade transformiert werden, enden mit einem grossen T. So bezeichnet StateT oder MaybeT die State bzw. Maybe Monade, die mit einer Monade parametrisiet werden.
Das Ergebnis der inneren Monade wird in der zusätzlichen, äußeren Monade gekapselt. Dies lässt sich aus den Signaturen der Funktionen (runState versus runStateT) ablesen.
runState :: State s a -> s -> (a, s)
runStateT :: Monad m => StateT s m a -> s -> m (a, s)

  • um die monadischen Funktionen der inneren Monade in der äußeren Monade zu verwenden, hilft die liftFunktion
    import Control.Monad.State

    incrAndPrint :: StateT Integer IO ()
    incrAndPrint = do
    modify (+1)
    x <- get
    lift (print x)
  • die Funktion incAndPrint erhöht ihren Zustand in modify (+1) um 1 und gibt in lift (print x)den aktuellen Zustand aus
    *Main> runStateT incrAndPrint 4
    5
    ((),5)
  • die 5 ist die geliftete Ausgabe der IO Monade, das Tupel (Ergebnis,neuer Zustand) = ((),5) ist die Ausgabe der StateT Monade

Typischer Anwendungsfall

  • lies die Konfiguration der Anwendung (AppConfig) über die Kommandozeile oder auch Dateien (myApp) ein right IO Monade
  • stelle die Daten durch (getAppConfig) zur Verfügung und baue damit den Zustand des Programmes (evalState) auf rightState Monade
    data AppConfig = ...

    myApp :: StateT AppConfig IO ()
    myApp = ...

    main :: IO ()
    main = do
    cfg <- getAppConfig
    evalStateT myApp cfg

Interessante Anwendungen

Parser

Software Transactional Memory

Literatur

Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare