Monaden Monaden Gliederung Funktionale Programmierung Monaden 1 D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg Monaden Klasse Monad Tree Maybe Liste MonadPlus c Sommer 2014, 16. Mai 2014, 2011-14 D.Rösner D. Rösner FP 2014 . . . Monaden 1 D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus Monaden Monaden in Haskell 2 Klasse Monad Tree Maybe Liste MonadPlus Die Klasse Monad Verallgemeinerung der Prinzipien von I/O die Signatur umfasst vier Grundoperationen Möglichkeit zum Sequentialisieren von Berechnungen drei relevante Klassen: class Monad m return :: (>>=) :: (>>) :: fail :: die Klasse Functor (aus dem Standard Prelude) die Klasse Monad (aus dem Standard Prelude) die Klasse MonadPlus (aus Bibliothek Monad) D. Rösner FP 2014 . . . 4 where a -> m m a -> m a -> String a (a -> m b) -> m b m b -> m b -> m a D. Rösner FP 2014 . . . 5 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Die Klasse Monad cont. Klasse Monad Tree Maybe Liste MonadPlus Die Klasse Monad cont. zur Verwendung der vier Grundoperationen: mit return wird ein Wert als monadischer Wert ohne weitere Aktion zurückgegeben mit (>>=) werden Berechnungen sequentialisiert, wobei ein Wert von der einen zur anderen Berechnung weitergereicht wird mit (>>) werden Berechnungen sequentialisiert, wobei die zweite den Wert der ersten Berechnung nicht benötigt fail s ist eine Berechnung, die fehlschlägt und die Fehlermeldung s liefert für (>>) (sprich: sequence) und fail gibt es Default-Implementationen: p >> q fail s = p >>= \ _ -> q = error s für Instanzen reicht es meist aus, (>>=) (sprich: bind oder then) und return zu definieren D. Rösner FP 2014 . . . Monaden 6 Klasse Monad Tree Maybe Liste MonadPlus Monaden Die Klasse Monad cont. 7 Klasse Monad Tree Maybe Liste MonadPlus Die Klasse Monad cont. Instanzen der Klasse Monad müssen bestimmten Gesetzen genügen Definition des abgeleiteten Operator >@> (sog. Kleisli-Komposition): mit dem abgeleiteten Operator >@> (sog. Kleisli-Komposition) lauten diese: M1: M2: M3: D. Rösner FP 2014 . . . (>@>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c) return >@> f = f f >@> return = f (f >@> g) >@> h = f >@> (g >@> h) f >@> g = \x -> (f x) >>= g m.a.W.: Verallgemeinerung der Funktionskomposition return ist ein neutrales Element (oder Identität) für >@> (M1, M2) und >@> ist assoziativ (M3) D. Rösner FP 2014 . . . 8 D. Rösner FP 2014 . . . 9 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Die Klasse Monad cont. Beziehung zwischen Monaden und do-Notation mit (»=) lauten die Monadengesetze wie folgt: M1’: M2’: M3’: die do-Notation ist nur eine Kurzschreibweise für die Anwendung der monadischen Operatoren einige der dabei geltenden Übersetzungsregeln: return a >>= k = k a m >>= return = m (m >>= k) >>= h = m >>= (\x -> k x >>= h) do e1 ==> e1 do e1; e2; ...; eN ==> e1 >> do e2; ...; eN do x <- e1; e2; ...; eN ==> e1 >>= \x -> do e2; ...; eN Interpretation M1’: return schickt seinen Wert an die nächste Aktion M2’: wenn eine Aktion direkt von einem return gefolgt, dann kann auch die Aktion selbst den Wert zurückgeben M3’: eine Form von Assoziativität von (»=) D. Rösner FP 2014 . . . Monaden 10 Klasse Monad Tree Maybe Liste MonadPlus Definitionen für zumindest (>>=) und return (die den Monadengesetzen genügen), die spezialisierten Typen dieser Operationen, eine Interpretation der besonderen Eigenschaften der Monade als Berechnung. dem Assoziativgesetz für (>>) entspricht: do (do m1; m2); m3 D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus generell gilt: Elemente einer Monade m a sind Berechnungen, die gewisse Aktionen ausführen können, bevor sie einen Wert vom Typ a zurückgeben (vgl. [Tho99], p. 404]) für unterschiedliche Instanzen der Klasse Monad sind anzugeben: do x <- return a; k x = k a do x <- m; return x = m do x <- m; y <- k x; h y = do y <- (do x <- m; k x); h y = 11 Instanzen der Klasse Monad die Monadengesetze nehmen dann die folgende Gestalt an (vgl. [Hud00]): do m1; m2; m3 D. Rösner FP 2014 . . . Monaden Beziehung zwischen Monaden und do-Notation M1”: M2”: M3”: Klasse Monad Tree Maybe Liste MonadPlus 12 D. Rösner FP 2014 . . . 13 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Anwendung von Monad auf Bäume [cf. [Tho99], Ch. 18.9] Klasse Monad Tree Maybe Liste MonadPlus Aufgabe: Nummerieren eines beliebigen Baumes binäre Bäume als rekursive Datentypen Aufgabe numTree: ersetze die Knoten eines beliebigen Baumes durch Zahlen; dabei sollen identische Knoten jeweils durch dieselbe Zahl ersetzt werden (cf. [Tho99], pp. 409) data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Eq, Ord, Show, Read) ein Beispiel eines Baumes: tree1 = Node "Moon" (Node "Ahmet" Nil Nil) (Node "Dweezil" (Node "Ahmet" Nil Nil) (Node "Moon" Nil Nil)) D. Rösner FP 2014 . . . Monaden 15 Klasse Monad Tree Maybe Liste MonadPlus D. Rösner FP 2014 . . . Monaden Aufgabe: Nummerieren eines beliebigen Baumes 16 Klasse Monad Tree Maybe Liste MonadPlus Details der direkten Lösung -- a Table associates elements with zero-based positions type Table a = [a] eine mögliche Lösung: -- traverse a tree and return the Table of its elements traversiere den Baum und kreiere dabei eine Tabelle, die Knoten mit ganzen Zahlen assoziiiert traversiere den Baum erneut mit dieser Tabelle und ersetze jeden Knoten durch die assoziiierte Zahl tree2table (Node root left right) = t2t right (t2t left [root]) where t2t tree table = case tree of Nil -> table (Node root1 l1 r1) -> t2t r1 (t2t l1 (update root1 table)) Nachteil dieser Lösung: Baum wird zweimal traversiert D. Rösner FP 2014 . . . 17 D. Rösner FP 2014 . . . 18 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Details der direkten Lösung Klasse Monad Tree Maybe Liste MonadPlus Details der Lösung -- a direct implementation of numTree Aktualisieren der Tabelle, falls neuer Knoten gefunden: numTree’ tree = convert tree (tree2table tree) where convert tree table = case tree of Nil -> Nil (Node root1 l1 r1) -> (Node (lookup’ root1 table) (convert l1 table) (convert r1 table)) update node table = if elem node table then table else table ++ [node] Anwendung: Main> tree2table tree1 ["Moon","Ahmet","Dweezil"] D. Rösner FP 2014 . . . Monaden 19 Klasse Monad Tree Maybe Liste MonadPlus D. Rösner FP 2014 . . . Monaden Details der direkten Lösung 20 Klasse Monad Tree Maybe Liste MonadPlus Details der direkten Lösung -- lookup is in Prelude.hs lookup’ elem tableG -- for the error message (cf. below) = look elem tableG 0 where look elem table pos = case table of [] -> error ("from lookup’: table " ++ show tableG ++ " does not contain elem " ++ show elem) x:xs -> if x == elem then pos else look elem xs (pos + 1) D. Rösner FP 2014 . . . 21 Main> tree1 Node "Moon" (Node "Ahmet" Nil Nil) (Node "Dweezil" (Node "Ahmet" Nil Nil) (Node "Moon" Nil Nil)) Main> numTree’ tree1 Node 0 (Node 1 Nil Nil) (Node 2 (Node 1 Nil Nil) (Node 0 Nil Nil)) D. Rösner FP 2014 . . . 22 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Eine monadische Lösung [cf. [Tho99], Ch. 18.9] Eine monadische Lösung Grundidee: ein Zustand enthält eine Funktion, die einer Tabelle (mit Werten vom Typ a) ein Paar zuordnet aus einer möglicherweise veränderten Tabelle und einem Ergebniswert (vom Typ b) ein Objekt x wird in eine Monadeninstanz als Funktion \tab -> (tab,x) eingebracht return x = State (\tab -> (tab,x)) Erläuterung zur Definition von >>= data State a b = State (Table a -> (Table a, b)) die Anwendung der Funktion st aus (State st) auf eine Tabelle tab liefert ein Paar (newTab,y) wird f auf y angewendet, so ergibt dies eine Monadeninstanz (State trans) die Funktion trans aus dieser Monade wird dann auf newTab angewendet instance Monad (State a) where return x = State (\tab -> (tab,x)) (State st) >>= f = State (\tab -> let (newTab,y) = st tab (State trans) = f y in trans newTab) D. Rösner FP 2014 . . . Monaden Klasse Monad Tree Maybe Liste MonadPlus 23 Klasse Monad Tree Maybe Liste MonadPlus D. Rösner FP 2014 . . . Monaden Eine monadische Lösung 24 Klasse Monad Tree Maybe Liste MonadPlus Eine monadische Lösung numberNode x = State (nNode x) numberTree Nil = return Nil nNode x table | elem x table = (table , lookup’ x table) | otherwise = (table++[x], length table) numberTree (Node x t1 t2) = do num <nt1 <nt2 <return Main> :t numberNode numberNode :: (Show a, Eq a) => a -> State a Int numberNode x numberTree t1 numberTree t2 (Node num nt1 nt2) Main> :t numberTree numberTree :: (Eq a, Show a) => Tree a -> State a (Tree Int) Main> :t nNode nNode :: (Eq a, Show a) => a -> [a] -> ([a],Int) D. Rösner FP 2014 . . . 25 D. Rösner FP 2014 . . . 26 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Eine monadische Lösung Klasse Monad Tree Maybe Liste MonadPlus Eine monadische Lösung den Rückgabewert erhält man, indem man Frage: wie lässt sich do num <nt1 <nt2 <return die im Zustand (State st) enthaltene Funktion st auf eine leere Tabelle anwendet; dies liefert ein Paar aus (veränderter) Tabelle und dem Rückgabewert als zweites Element extract (State st) = snd (st []) numberNode x numberTree t1 numberTree t2 (Node num nt1 nt2) schreiben mit Operator >>= ? D. Rösner FP 2014 . . . Monaden numTree :: (Show a, Eq a) => Tree a -> Tree Int numTree = extract . numberTree 27 Klasse Monad Tree Maybe Liste MonadPlus D. Rösner FP 2014 . . . Monaden Eine monadische Lösung 28 Klasse Monad Tree Maybe Liste MonadPlus Variante: gleichzeitig Baum nummerieren und spiegeln numberAndMirrorTree Nil = return Nil Verwendung: numberAndMirrorTree (Node x t1 t2) = do num <- numberNode x nt1 <- numberAndMirrorTree t1 nt2 <- numberAndMirrorTree t2 return (Node num nt2 nt1) -- mirroring! Main> numTree tree1 Node 0 (Node 1 Nil Nil) (Node 2 (Node 1 Nil Nil) (Node 0 Nil Nil)) numAndMirrorTree :: (Show a, Eq a) => Tree a -> Tree Int numAndMirrorTree = extract . numberAndMirrorTree D. Rösner FP 2014 . . . 29 D. Rösner FP 2014 . . . 30 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Variante: gleichzeitig Baum nummerieren und spiegeln numTree: Vergleich mit Lösung ohne Monaden numTree’’ tree = ntree where (ntree, table) = traverseConvert (tree,[]) traverseConvert pair = case pair of (Nil, table) -> (Nil, table) ((Node root1 l1 r1), table) -> ((Node (lookup’ root1 tabler1) nl1 nr1), tabler1) where (nl1, tablel1) = traverseConvert (l1, (update root1 table)) (nr1, tabler1) = traverseConvert (r1, tablel1) Main> numAndMirrorTree tree1 Node 0 (Node 2 (Node 0 Nil Nil) (Node 1 Nil Nil)) (Node 1 Nil Nil) Main> tree1 Node "Moon" (Node "Ahmet" Nil Nil) (Node "Dweezil" (Node "Ahmet" Nil Nil) (Node "Moon" Nil Nil)) Main> numTree tree1 Node 0 (Node 1 Nil Nil) (Node 2 (Node 1 Nil Nil) (Node 0 Nil Nil)) D. Rösner FP 2014 . . . Monaden Klasse Monad Tree Maybe Liste MonadPlus 31 D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus Monaden Lösung ohne Monaden 32 Klasse Monad Tree Maybe Liste MonadPlus Zusammenfassung die monadische Lösung erlaubt es, die Behandlung der Tabelle in den Zuständen und den Zustandsübergängen zu ‘verbergen’ Wie ist numTree” abzuändern für eine nonmonadische Lösung für numAndMirrorTree? ohne Monaden müssen die Zustände explizit zusammen mit der eigentlichen Aufgabenstellung bearbeitet werden m.a.W.: Monaden als Mittel zur Abstraktion und Modularisierung D. Rösner FP 2014 . . . 33 D. Rösner FP 2014 . . . 34 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Maybe als Instanz der Klasse Monad Maybe als Monad cont. wollte man zwei Funktionen f :: a -> b und g :: b -> c hintereinander ausführen, bei denen jeweils auch Fehler auftreten können und daher die Resultate mit dem Datentyp Maybe angegeben werden, so wäre ohne monadische Operatoren die Fehlerbehandlung bei g (f x) z.B. wie folgt (vgl. [Hud00], p. 256): Elemente der Monade Maybe a sind Berechnungen, die entweder einen Wert vom Typ a haben oder einen Fehler produzieren können aus dem Standard Prelude: instance Monad Maybe where Just x >>= k = k x Nothing >>= k = Nothing return = Just fail s = Nothing D. Rösner FP 2014 . . . Monaden Klasse Monad Tree Maybe Liste MonadPlus case (f x) of Nothing -> Nothing Just y -> case (g y) of Nothing -> Nothing Just z -> z D. Rösner FP 2014 . . . 36 Klasse Monad Tree Maybe Liste MonadPlus Monaden Maybe als Monad cont. 37 Klasse Monad Tree Maybe Liste MonadPlus Maybe als Monad cont. Vereinfachungen (vgl. [Hud00], pp. 256/257): f x >>= \y -> g y >>= \z -> return z mit monadischen Operatoren vereinfacht sich dies zu: sog. ’currying simplication’ f x >>= \y -> g y >>= \z -> return z f x >>= \y -> g y >>= return in do-Notation: Monadengesetz für return do y <- f x; z <- g y; return z f x >>= \y -> g y erneut ’currying simplication’ f x >>= g D. Rösner FP 2014 . . . 38 D. Rösner FP 2014 . . . 39 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Maybe als Monad cont. Klasse Monad Tree Maybe Liste MonadPlus Datentyp Liste als Instanz von Monad m.a.W.: aus g(f x) wurde f x >>= g aus dem Standard Prelude: weitere Abstraktion als monadischer Kompositionsoperator (vgl. [Hud00], p. 257): instance Monad [ ] where (x:xs) >>= f = f x ++ (xs >>= f) [] >>= f = [] return x = [x] fail s = [] composeM :: Monad m => (b -> mc) -> (a -> mb) -> (a -> mc) gleichwertige Definition: (g ‘composeM‘ f) x = f x >>= g D. Rösner FP 2014 . . . Monaden xs >>= f = concat (map f xs) 40 Klasse Monad Tree Maybe Liste MonadPlus D. Rösner FP 2014 . . . Monaden Datentyp Liste als Instanz von Monad 42 Klasse Monad Tree Maybe Liste MonadPlus Datentyp Liste als Instanz von Monad die Typen der monadischen Operatoren sind dann: die folgenden Darstellungen sind damit gleichwertig: (>>=) :: [a] -> (a -> [b]) -> [b] do x <- [1,2,3]; y <- [4,5,6]; return (x,y) return :: a -> [a] die Monade [] lässt sich als Realisation nichtdeterministischer Berechnungen sehen: [1,2,3] >>= (\x -> [4,5,6] >>= (\y -> return (x,y))) [(x,y) | x <- [1,2,3], y <- [4,5,6]] ein Element von [a] repräsentiert alle Ergebnisse einer Berechnung Fehlschlag ergibt eine leere Resultatliste D. Rösner FP 2014 . . . 43 D. Rösner FP 2014 . . . 44 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Motivation: Umgang mit Alternativen Klasse Monad Tree Maybe Liste MonadPlus Motivation: Umgang mit Alternativen Beispiel: simple DB mit Telefonnummern von Personen (cf. [OGS09], Ch. 15) Annahme: für persönliches Telefonat die private ode mobile Nummer gesucht, nicht aber die geschäftliche -- file: ch15/VCard.hs data Context = Home | Mobile | Business deriving (Eq, Show) -- file: ch15/VCard.hs onePersonalPhone :: [(Context, Phone)] -> Maybe Phone onePersonalPhone ps = case lookup Home ps of Nothing -> lookup Mobile ps Just n -> Just n type Phone = String albulena = [(Home, "+355-652-55512")] nils = [(Mobile, "+47-922-55-512"), (Business, "+47-922-12-121"), (Home, "+47-925-55-121"), (Business, "+47-922-25-551")] cf. [OGS09], Ch. 15 twalumba = [(Business, "+260-02-55-5121")] D. Rösner FP 2014 . . . Monaden 46 D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus Monaden Motivation: Umgang mit Alternativen Klasse Monad Tree Maybe Liste MonadPlus Motivation: Umgang mit Alternativen beide Funktionen haben gleiche Struktur im case: Verwendung von Liste, um mehrere Nummern zurückgeben zu können zuerst leeres Resultat behandeln, dann nicht-leeres Resultat -- file: ch15/VCard.hs allBusinessPhones :: [(Context, Phone)] -> [Phone] allBusinessPhones ps = map snd numbers where numbers = case filter (contextIs Business) ps of [] -> filter (contextIs Mobile) ps ns -> ns Verwendung: contextIs a (b, _) = a == b ghci> onePersonalPhone twalumba Nothing ghci> onePersonalPhone albulena Just "+355-652-55512" ghci> allBusinessPhones nils ["+47-922-12-121","+47-922-25-551"] cf. [OGS09], Ch. 15 cf. [OGS09], Ch. 15 D. Rösner FP 2014 . . . 47 48 D. Rösner FP 2014 . . . 49 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Motivation: Umgang mit Alternativen Motivation: Umgang mit Alternativen Definitionen von [] und Maybe als Instanzen von MonadPlus (cf. [OGS09], Ch. 15) -- file: instance mzero mplus Redefinition: -- file: ch15/VCard.hs oneBusinessPhone :: [(Context, Phone)] -> Maybe Phone oneBusinessPhone ps = lookup Business ps ‘mplus‘ lookup Mobile ps ch15/VCard.hs MonadPlus [] where = [] = (++) allPersonalPhones :: [(Context, Phone)] -> [Phone] allPersonalPhones ps = map snd $ filter (contextIs Home) ps ‘mplus‘ filter (contextIs Mobile) ps instance MonadPlus Maybe where mzero = Nothing Nothing ‘mplus‘ ys = ys xs ‘mplus‘ _ = xs cf. [OGS09], Ch. 15 D. Rösner FP 2014 . . . Monaden Klasse Monad Tree Maybe Liste MonadPlus 50 D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus Monaden Motivation: Umgang mit Alternativen 51 Klasse Monad Tree Maybe Liste MonadPlus Motivation: Umgang mit Alternativen Redefinition als lookupM für beliebige Instanzen von MonadPlus: normales lookup: -- file: ch15/VCard.hs lookup :: (Eq a) => a -> [(a, b)] -> Maybe b lookup _ [] = Nothing lookup k ((x,y):xys) | x == k = Just y | otherwise = lookup k xys -- file: ch15/VCard.hs lookupM :: (MonadPlus m, Eq a) => a -> [(a, b)] -> m b lookupM _ [] = mzero lookupM k ((x,y):xys) | x == k = return y ‘mplus‘ lookupM k xys | otherwise = lookupM k xys cf. [OGS09], Ch. 15 cf. [OGS09], Ch. 15 D. Rösner FP 2014 . . . 52 D. Rösner FP 2014 . . . 53 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Motivation: Umgang mit Alternativen Die Klasse MonadPlus manche Monaden besitzen eine Operation mplus, mit der monadische Werte aus getrennten Berechnungen in einen monadischen Wert kombiniert werden können, und ein neutrales Element mzero für diesen Operator es müssen dann die folgenden zusätzlichen Gesetze gelten: Beachte: mplus ist nicht immer eine ’Addition’ Prelude> import Control.Monad Prelude Control.Monad> [1,2,3] ‘mplus‘ [4,5,6] [1,2,3,4,5,6] Prelude Control.Monad> Just 1 ‘mplus‘ Just 2 Just 1 M+1: M+2: M+3: M+1: cf. [OGS09], Ch. 15 D. Rösner FP 2014 . . . Monaden Klasse Monad Tree Maybe Liste MonadPlus 54 Klasse Monad Tree Maybe Liste MonadPlus mzero >>= f = mzero m >>= (\x -> mzero) = mzero mzero ‘mplus‘ m = m m ‘mplus‘ mzero = m D. Rösner FP 2014 . . . Monaden Die Klasse MonadPlus 55 Klasse Monad Tree Maybe Liste MonadPlus Instanzen der Klasse MonadPlus Maybe wird zur Instanz der Klasse MonadPlus durch: Monaden mit mplus und mzero sollten als Instanzen der Klasse MonadPlus deklariert werden instance MonadPlus Maybe mzero = Nothing ‘mplus‘ ys = xs ‘mplus‘ ys = Definition: (aus Monad.hs) class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a D. Rösner FP 2014 . . . where Nothing ys xs m.a.W. ’Addieren’ zweier Werte vom Typ Maybe ergibt den ersten Wert, der nicht Nothing, oder Nothing, falls beide Werte Nothing 56 D. Rösner FP 2014 . . . 57 Monaden Klasse Monad Tree Maybe Liste MonadPlus Monaden Instanzen der Klasse MonadPlus Literatur: I Paul Hudak. The Haskell School of Expression – Learning Functional Programming through Multimedia. Cambridge University Press, Cambridge, UK, 2000. ISBN 0-521-64338-4. bei der Monade für Listen ist mzero die leere Liste und die ’Addition’ mplus ist die Listenkonkatenation Definition: instance MonadPlus [ ] where mzero = [] mplus = (++) D. Rösner FP 2014 . . . Monaden Bryan O’Sullivan, John Goerzen, and Don Stewart. Real World Haskell. O’Reilly Media, Sebastopol, CA 95472, 2009. ISBN 978-0-596-51498-3; online available at http://book.realworldhaskell.org/. 58 Klasse Monad Tree Maybe Liste MonadPlus Literatur: II Simon Thompson. Haskell - The Craft of Functional Programming. Addison Wesley Longman Ltd., Essex, 1999. 2nd edition, ISBN 0-201-34275-8; Accompanying Web site: http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e. D. Rösner FP 2014 . . . Klasse Monad Tree Maybe Liste MonadPlus 60 D. Rösner FP 2014 . . . 59