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 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++
- const als Code Anforderung const correctness
- Scott Meyers verwendet die Template Metaprogramming Bibliothek mpl von boost, um Code Anforderungen zur Compilezeit sicherzustellen (thread safe, side-effect free, exception safe, portable) Enforcing Code Feature Requirements in 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 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 hässlicher Code
- das Ergebnis der Funktion ist die letzte case Anweisung Nothing 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 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
- Nothing >>= f = Nothing: propagiert das Fehlen eines Ergebnisses weiter
- 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
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 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
- Interaktiv in der Haskell Shell
Axiome
bind und returneiner Monade müssen folgenden Gesetzen genügen.- return x >>= f == f x
- return ist linksneutral
- c >>= return == c
- return ist rechtsneutral
- 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 die IO Monade besizt implizit den Zustand der Welt
- das Hauptprogramm besitzt in Haskell die Deklaration: main :: IO () 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 = []
- Berechnungen über Sequenzen unbekannter Länge
- 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
- 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] - do Notation
generateList= do
a <- [1,2,3]
b <- [10,100,100]
return (a^2 + b^2) - List comprehension
Prelude> [ a^2+b^2 | a <- [1,2,3], b <- [10,100,1000] ]
[101,10001,1000001,104,10004,1000004,109,10009,1000009]
- klassische Monaden Notation
State
- Problem:
- ein Programm muß während seiner Ausführung einen modifizierbaren Zustand aufbauen, auf den es zugreifen kann. 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 ()
- Berechnung über Sequenzen von Operationen, die einen gemeinsamen Zustand benötigen
- 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")
- 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
- 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)
- 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)
- der linear congruential generator erzeugt zu einem Zustand s0 eine Paar Zufallszahl und neuer Zustand (output,s1)
- 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
- 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
- ein klassischer Anwendungsfall für die State Monade ist das Erzeugen von Zufallswerten
- get und put in Aktion
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
- Instanzen der speziellen Monade MonadPlus definieren Methoden mzero und mplusfür das Fehlen eines Ergebnisses einer Berechnung und die bedingte Berechnung eines neuen Wertes
- 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 bDiese 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.
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 IO Monade
- stelle die Daten durch (getAppConfig) zur Verfügung und baue damit den Zustand des Programmes (evalState) auf State Monade
data AppConfig = ...
myApp :: StateT AppConfig IO ()
myApp = ...
main :: IO ()
main = do
cfg <- getAppConfig
evalStateT myApp cfg
Interessante Anwendungen
Parser
Software Transactional Memory
Literatur
- Einführung Understanding Haskell Monads von Ertugrul Söylemez
- Umfassender Überblick All About Monads von Jeff Newber
- Monadische Funktionen A tour of the haskell monadic functions von Henk-Jan van Tuyl
Weiterlesen...