Programmieren in Haskell Monaden Programmieren in Haskell 1
Sequenzierung mit Dollar und Euro Die Atommüll-Metapher Maybe- und Listen-Monaden Return Die do-notation Monaden als Berechnungen Listenbeschreibungen und Monaden State-Monaden Die IO-Monade Was wir heute machen Programmieren in Haskell 2
Dollar i (h (g (f x))) i $ h $ g $ f x Programmieren in Haskell 3
Dollar i (h (g (f x))) i $ h $ g $ f x infixr 0 $ ($) :: (a -> b) -> a -> b f $ x = f x Programmieren in Haskell 3
Euro infixl 0 > ( >) :: a -> (a -> b) -> b x > f = f x i $ h $ g $ f x f x > g > h > i f x > g > h > i Programmieren in Haskell 4
Atommüll-Metapher Abfall-Prozessoren sind eine Metapher für Funktionen. Sie nehmen Atommüll, verarbeiten ihn und spucken Atommüll aus: Programmieren in Haskell 5
Abfall-Container Atommüll ist gefährlich, also wird er verpackt: Programmieren in Haskell 6
bind-roboter Ein bind-roboter entpackt einen Container und steckt den Inhalt (Atommüll) in einen Prozessor: Programmieren in Haskell 7
bind (>>=) container >>= fn = let a = extractwaste container in fn a (>>=) :: m a -> (a -> m b) -> m b Programmieren in Haskell 8
bind (>>=) container >>= fn = let a = extractwaste container in fn a (>>=) :: m a -> (a -> m b) -> m b Mit dem bind-roboter können wir Prozessoren hintereinanderschalten: wasteinacontainer >>= (\a1 -> putincontainer (decompose a1)) >>= (\a2 -> putincontainer (decay a2)) >>= (\a3 -> putincontainer (melt a3)) Programmieren in Haskell 8
Bisherige Metaphern 1. Prozessoren (Funktionen) 2. Atommüll (Eingaben) 3. Container (monadische Werte) 4. Der bind-roboter >>= 5. Fabriken (Monaden) Programmieren in Haskell 9
Die Maybe-Monade Zur Erinnerung der Maybe-Datentyp: data Maybe a = Nothing Just a Programmieren in Haskell 10
Die Maybe-Monade Zur Erinnerung der Maybe-Datentyp: data Maybe a = Nothing Just a Die bind-funktion: container >>= fn = case container of Nothing -> Nothing Just a -> fn a (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b Programmieren in Haskell 10
Die Listen-Monade container >>= fn = case container of [] -> [] xs -> concat (map fn xs) (>>=) :: [a] -> (a -> [b]) -> [b] Programmieren in Haskell 11
Return Was machen wir mit Maschinen, die direkt Atommüll ausgeben ohne ihn vorher in Container zu verpacken? return :: a -> m a Das ist unsere Funktion putincontainer von vorhin. Programmieren in Haskell 12
Return Was machen wir mit Maschinen, die direkt Atommüll ausgeben ohne ihn vorher in Container zu verpacken? return :: a -> m a Das ist unsere Funktion putincontainer von vorhin. Die return-funktion für Maybe: return a = Just a Programmieren in Haskell 12
Return Was machen wir mit Maschinen, die direkt Atommüll ausgeben ohne ihn vorher in Container zu verpacken? return :: a -> m a Das ist unsere Funktion putincontainer von vorhin. Die return-funktion für Maybe: return a = Just a und für Listen: return a = [a] Programmieren in Haskell 12
Die do-notation Monadischer Code wie bisher: wasteinacontainer >>= \a1 -> foo a1 >>= \a2 -> bar a2 >>= \a3 -> baz a3 Programmieren in Haskell 13
Die do-notation Monadischer Code wie bisher: wasteinacontainer >>= \a1 -> foo a1 >>= \a2 -> bar a2 >>= \a3 -> baz a3 Etwas anders geschrieben: wasteinacontainer >>= \a1 -> foo a1 >>= \a2 -> bar a2 >>= \a3 -> baz a3 Programmieren in Haskell 13
Die do-notation Monadischer Code wie bisher: wasteinacontainer >>= \a1 -> foo a1 >>= \a2 -> bar a2 >>= \a3 -> baz a3 Etwas anders geschrieben: wasteinacontainer >>= \a1 -> foo a1 >>= \a2 -> bar a2 >>= \a3 -> baz a3 Und mit syntaktischem Zucker: do a1 <- wasteinacontainer a2 <- foo a1 a3 <- bar a2 baz a3 Programmieren in Haskell 13
Monaden als Berechnungen Unserer bisherige Metapher sah Monaden als Container. Jetzt interpretieren wir Monaden als Berechnungen: >>=: verkettet zwei monadische Berechnungen (lässt die erste Berechnung laufen, füttert das Ergebnis in die zweite Berechung und lässt auch diese laufen) return x: die Berechung, die einfach x als Ergebnis hat Programmieren in Haskell 14
Rechnen mit der Maybe-Monade Beginnen wir mit einem Telefonbuch: phonebook :: [(String,String)] phonebook = [ ("Bob", "01788 665242"), ("Fred", "01624 556442"), ("Alice", "01889 985333"), ("Jane", "01732 187565") ] Programmieren in Haskell 15
Rechnen mit der Maybe-Monade Beginnen wir mit einem Telefonbuch: phonebook :: [(String,String)] phonebook = [ ("Bob", "01788 665242"), ("Fred", "01624 556442"), ("Alice", "01889 985333"), ("Jane", "01732 187565") ] Darin können wir nach Namen und dazu gespeicherten Telefonnummern suchen: lookup :: a -- Schluessel -> [(a, b)] -- Lookup-Tabelle -> Maybe b -- Ergebnis des Lookups Programmieren in Haskell 15
Programmieren in Haskell 16
Beispiel: Prelude> lookup "Bob" phonebook Just "01788 665242" Prelude> lookup "Jane" phonebook Just "01732 187565" Prelude> lookup "Zoe" phonebook Nothing Programmieren in Haskell 16
Kombination von Lookups comb :: Maybe a -> (a -> Maybe b) -> Maybe b comb Nothing _ = Nothing comb (Just x) f = Just $ f x Programmieren in Haskell 17
Kombination von Lookups comb :: Maybe a -> (a -> Maybe b) -> Maybe b comb Nothing _ = Nothing comb (Just x) f = Just $ f x comb ist nichts anderes als der bind-operator (>>=) für Maybe-Berechnungen. Also: gettaxowed :: String -- Name -> Maybe Double -- Steuerschuld gettaxowed name = lookup name phonebook >>= (\number -> lookup number governmentaldatabase) >>= (\registration -> lookup registration taxdatabase) Programmieren in Haskell 17
Kombination von Lookups comb :: Maybe a -> (a -> Maybe b) -> Maybe b comb Nothing _ = Nothing comb (Just x) f = Just $ f x comb ist nichts anderes als der bind-operator (>>=) für Maybe-Berechnungen. Also: gettaxowed :: String -- Name -> Maybe Double -- Steuerschuld gettaxowed name = lookup name phonebook >>= (\number -> lookup number governmentaldatabase) >>= (\registration -> lookup registration taxdatabase) Oder in der do-notation: Programmieren in Haskell 17
gettaxowed name = do number <- lookup name phonebook registration <- lookup number governmentaldatabase lookup registration taxdatabase Programmieren in Haskell 18
Die weiteren Tabellen: governmentaldatabase :: [(String,String)] governmentaldatabase = [ ("01788 665242", "B24869237"), ("01624 556442", "F36636467"), ("01889 985333", "A22121254"), ("01732 187565", "J77848449") ] taxdatabase :: [(String,String)] taxdatabase = [ ("B24869237", "lots of"), ("F36636467", "not so much"), ("A22121254", "gets something back"), ("J77848449", "criminal") ] Programmieren in Haskell 19
do number <- lookup "Bob" phonebook registration <- lookup number governmentaldatabase taxowed <- lookup registration taxdatabase return taxowed => Just "lots of" Programmieren in Haskell 20
do number <- lookup "Bob" phonebook registration <- lookup number governmentaldatabase taxowed <- lookup registration taxdatabase return taxowed => Just "lots of" do number <- lookup "Zoe" phonebook registration <- lookup number governmentaldatabase taxowed <- lookup registration taxdatabase return taxowed Programmieren in Haskell 20
do number <- lookup "Bob" phonebook registration <- lookup number governmentaldatabase taxowed <- lookup registration taxdatabase return taxowed => Just "lots of" do number <- lookup "Zoe" phonebook registration <- lookup number governmentaldatabase taxowed <- lookup registration taxdatabase return taxowed => Nothing Programmieren in Haskell 20
Eigenschaften der Maybe-Monade Die Maybe-Monade repräsentiert Berechnungen, die versagen können Versagen wird propagiert! Programmieren in Haskell 21
Die Listen-Monade Berechnungen in der Listen-Monade reprässentieren null oder mehr gültige Antworten: instance Monad [] where return a = [a] xs >>= f = concat (map f xs) Programmieren in Haskell 22
Tic-Tac-Toe Wir wollen alle Brett-Konfigurationen ausrechnen, die sich nach drei Zügen ergeben können: getnextconfigs :: Board -> [Board] getnextconfigs =... -- details not important tick :: [Board] -> [Board] tick bds = concatmap getnextconfigs bds find3rdconfig :: Board -> [Board] find3rdconfig bd = tick $ tick $ tick [bd] Programmieren in Haskell 23
Tic-Tac-Toe Wir wollen alle Brett-Konfigurationen ausrechnen, die sich nach drei Zügen ergeben können: getnextconfigs :: Board -> [Board] getnextconfigs =... -- details not important tick :: [Board] -> [Board] tick bds = concatmap getnextconfigs bds find3rdconfig :: Board -> [Board] find3rdconfig bd = tick $ tick $ tick [bd] Mit der Listen-Monade: Programmieren in Haskell 23
find3rdconfig :: Board -> [Board] find3rdconfig bd0 = do bd1 <- getnextconfigs bd0 bd2 <- getnextconifgs bd1 bd3 <- getnextconfigs bd2 return bd3 Programmieren in Haskell 24
Listenbeschreibungen und Monaden pythags = [ (x, y, z) x <- [1..], y <- [x..], z <- [y..], x^2 + y^2 == z^2 ] Programmieren in Haskell 25
Listenbeschreibungen und Monaden pythags = [ (x, y, z) x <- [1..], y <- [x..], z <- [y..], x^2 + y^2 == z^2 ] pythags = do x <- [1..] y <- [x..] z <- [y..] guard (x^2 + y^2 == z^2) return (x, y, z) Programmieren in Haskell 25
guard :: Bool -> [()] guard True = [()] guard False = [] Programmieren in Haskell 26
guard :: Bool -> [()] guard True = [()] guard False = [] pythags = let ret x y z = [(x, y, z)] gd x y z = concatmap (\_ -> ret x y z) (guard $ x^2 + y^2 == z^2) doz x y = concatmap (gd x y) [y..] doy x = concatmap (doz x ) [x..] dox = concatmap (doy ) [1..] in dox Programmieren in Haskell 26
start... x 1 2 3......... y 1 2 3 1 2 3 1 2 3........................... z 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 Programmieren in Haskell 27
Seiteneffekte mit der State-Monade Container werden mit einem Ticket gefüttert und geben ihren Inhalt und ein neues Ticket (eine Quittung) aus: Programmieren in Haskell 28
bind in der State-Monade Programmieren in Haskell 29
Input/Output Die IO-Monade ist eine State-Monade, in der der Zustand die komplette Umgebung ist. Dies erlaubt sequentielle Ein-/Ausgabe: f = do putstrln "What is your name?" name <- getline putstrln ("Nice to meet you, " ++ name ++ "!") Programmieren in Haskell 30
Input/Output Die IO-Monade ist eine State-Monade, in der der Zustand die komplette Umgebung ist. Dies erlaubt sequentielle Ein-/Ausgabe: f = do putstrln "What is your name?" name <- getline putstrln ("Nice to meet you, " ++ name ++ "!") Diese Konstruktion legt die Auswertungsreihenfolge fest! Programmieren in Haskell 30
Copyright notice http://en.wikibooks.org/wiki/haskell/understanding_monads http://en.wikibooks.org/wiki/haskell/advanced_monads http://en.wikibooks.org/wiki/haskell/monadplus Programmieren in Haskell 31