Programmieren in Haskell Stefan Janssen Programmieren in Haskell Monaden Imperative Programmierung IO Monaden Stefan Janssen Universität Bielefeld AG Praktische Informatik 14. Januar 2015 Imperative Programmierung in Haskell Programmieren in Haskell Stefan Janssen “Imperativ” nennt man Progamiersprachen, die mit Anweisungen operieren. Jede Anweisung hat Wirkungen auf einen globalen Zustand, der wiederum die nächsten Anweisugen beeinflusst. Solche imperativen Elemente braucht man auch in einer funktionalen Sprache, zum Beispiel zur Kommunikation mit der Außenwelt, sei es Ein- oder Ausgabe von Benutzerdaten, oder die Steuerung von Prozessen. Imperative Programmierung Allgemeines IO Monaden Was heißt “imperativ?” Programmieren in Haskell Stefan Janssen Ein imperatives Programm besteht aus Anweisungen. Sie modifizieren einen globalen Zustand (Speicherzellen, Variablen, Drucker, Datei, ), ... ... und hängen auch von ihm ab. imp. Funktionen sind oft keine Funktionen im mathematischen Sinn, Wert von f (x ) hängt nicht nur von x , sondern auch vom gerade vorliegenden Zustand ab, d.h. das Prinzip der “referential transparency” ist verletzt. Die Ausführungsreihenfolge ist wesentlich und im Detail festgelegt. Beispiel: URM-Programme, C, Pascal, Java, ... Imperative Programmierung Allgemeines IO Monaden Referential Transparency (Wiederholung) ein Funktionsaufruf hat keine Seiteneffekte bei gleichen Argumentwerten bleibt der Funktionswert gleich wesentliches Merkmal von (puren) funktionalen Programmiersprachen ⇒ Vorteile bei der Analyse (Korrektheit, Effizienz, . . . ) und Optimierung von Code Programmieren in Haskell Stefan Janssen Imperative Programmierung Allgemeines IO Monaden Referential Transparency (Wiederholung) ein Funktionsaufruf hat keine Seiteneffekte bei gleichen Argumentwerten bleibt der Funktionswert gleich wesentliches Merkmal von (puren) funktionalen Programmiersprachen ⇒ Vorteile bei der Analyse (Korrektheit, Effizienz, . . . ) und Optimierung von Code Beispiel für eine C-Funktion mit Seiteneffekten 1 int a = 0; 2 3 4 5 6 7 int f ( int x ) { a = a + 1; return x * x * a ; } Programmieren in Haskell Stefan Janssen Imperative Programmierung Allgemeines IO Monaden Funktionale Programmierung im Unterschied zur imperativen Programmieren in Haskell Stefan Janssen Funktionale Sprache: Es werden nur Datenabhängigkeiten beschrieben, Imperative Programmierung diese sind explizit (”referential transparency”), IO eine Berechnungsreihenfolge ist nicht festgelegt. Monaden Es gibt keine Anweisungen, keinen Zustand, kein vorher/nachher Kann man eine zustandsbasierte Rechnung simulieren, wenn man sie unbedingt braucht? Allgemeines Expliziter Zustand Programmieren in Haskell 1 2 3 Simulation der zustandsbasierten Rechnung: Expliziten Zustand s einführen. Statt z = f x y; r = g z nun überall Zustand angeben, z.B. (s ’ , z ) = f s x y (s ’ ’ , u ) = f s ’ x y (s ’ ’ ’ , r ) = g s ’ ’ z und ein Zustand s darf nie zweimal benutzt werden. Jedoch: Im “normalen” Haskell gibt es aber keine Hindernis, alle drei gleichzeitig zu benutzen Stefan Janssen Imperative Programmierung Allgemeines IO Monaden Erwünschte Zustände Programmieren in Haskell Stefan Janssen Wo wünscht man sich die imperative Programmierung: Ein-und Ausgabe sind inhärent zustandsabhängig und müssen in ihrer Abfolge koordiniert werden Funktionen mit “Nebenergebnis”, das überall behandelt werden muss, möchten dies in einem “Zustand” verstecken. Beispiel: Rechnen mit dem Typ Maybe Int Dafür gibt es in Haskell ein besonderes Konzept – Monaden – und eine spezielle Notation – do-Notation . Imperative Programmierung Allgemeines IO Monaden IO in Haskell – wie es nicht geht Programmieren in Haskell 1 2 Erster Ansatz wäre vielleicht: r e a d C h a rFromKeybo a r d :: Char r e ad I n tFromKeyboa r d :: Int Imperative Programmierung IO 3 4 Stefan Janssen stringToScreen :: String -> Bool Monaden IO in Haskell – wie es nicht geht Programmieren in Haskell 1 2 Erster Ansatz wäre vielleicht: r e a d C h a rFromKeybo a r d :: Char r e ad I n tFromKeyboa r d :: Int Imperative Programmierung IO 3 4 Stefan Janssen stringToScreen :: String -> Bool Probleme dabei Auswertungsreihenfolge Verletzung der Referential Transparency von Funktionen Monaden Auswertungsreihenfolge Programmieren in Haskell Stefan Janssen besonders bei Lazy-Evaluation Imperative Programmierung IO Monaden Auswertungsreihenfolge Programmieren in Haskell Stefan Janssen besonders bei Lazy-Evaluation Example f x = x + readInt Fr o m Ke y b oa r d - readInt F r om K e yb o a rd Imperative Programmierung IO Monaden Auswertungsreihenfolge Programmieren in Haskell Stefan Janssen besonders bei Lazy-Evaluation Example f x = x + readInt Fr o m Ke y b oa r d - readInt F r om K e yb o a rd printList [] = ? printList ( x : xs ) = stringToScreen x ...?... printList xs main ... = ... printList l Imperative Programmierung IO Monaden do-Notation Programmieren in Haskell Stefan Janssen do-Notation erlaubt Folge von Aktionen zu programmieren: Sie werden der Reihe nach ausgeführt können Werte berechnen und an Variable übergeben (aber nur einmal!) oder auch nur etwas ausführen Werte aus zuvor berechneten Variablen benutzen Imperative Programmierung IO Monaden Abstrakter Datentyp IO Programmieren in Haskell Stefan Janssen Ein- und Ausgabefunktionen sind “Aktionen” eines abstrakten Datentyps IO a werden importiert wie üblich: import System.IO Eingaben haben den Typ z.B. IO String oder IO Int Ausgabe-Aktionen liefern den Typ IO () Für die Verkettung von Aktionen gibt es eine besondere Notation Beispiel: do x <- getLine; print (length x) Imperative Programmierung IO Monaden Verkettung von “Aktionen” Programmieren in Haskell Stefan Janssen 1 > do 2 x <- act1 act2 y <- act3 x act4 x y return ( f x y ) Ausführung der Aktionen 1 - 4 in der angegebenen Reihefolge, Erzeugung und Wiederverwendung von Zwischenergebnissen 3 4 5 6 Imperative Programmierung IO Monaden IO-Beispiel: Ausgabe Programmieren in Haskell Stefan Janssen 1 putStr :: String -> IO () 2 3 putStrLn :: String -> IO () -- vordefiniert Imperative Programmierung IO 4 5 6 1 2 3 4 putStrLn str = do putStr str putStr ‘ ‘\n ’ ’ put4times str = do putStrLn str putStrLn str putStrLn str putStrLn str Monaden IO-Beispiel: Eingabe Programmieren in Haskell Stefan Janssen 1 2 getLine :: IO String getChar :: IO Char -- vordefiniert -- vordefiniert Imperative Programmierung 3 IO 4 5 6 7 8 read2lines :: IO () Monaden read2lines = do getLine getLine putStrLn " Two lines read and used for nothing " Die Zeilen werden zwar von der Eingabe entnommen, aber dann nicht weiter verwendet ... IO-Beispiel: Eingabeverarbeitung und Ausgabe Programmieren in Haskell Stefan Janssen 1 2 3 4 5 reverse2lines :: IO () reverse2lines = do line1 <- getLine line2 <- getLine putStrLn ( reverse line2 ) putStrLn ( reverse line1 ) Die eingelesenen Strings werden an Namen gebunden und können so in anderen Funktionen verwendet werden Imperative Programmierung IO Monaden Weitere IO-Funktionen Programmieren in Haskell Stefan Janssen Weitere IO-Funktionen für Lesen und Schreiben von Dateien Kommunikation mit dem Betriebssystem ... Imperative Programmierung IO Monaden IO-Funktionen Programmieren in Haskell 1 2 getChar getLine :: IO Char :: IO String 3 4 5 6 7 putChar putStr putStrLn print :: :: :: :: Char -> IO () String -> IO () String -> IO () Show a = > a -> IO () Stefan Janssen Imperative Programmierung IO Monaden IO-Funktionen Programmieren in Haskell 1 2 getChar getLine :: IO Char :: IO String 3 4 5 6 7 putChar putStr putStrLn print :: :: :: :: Char -> IO () String -> IO () String -> IO () Show a = > a -> IO () Wiederholung () ist der Unit-Datentyp, der nur den Wert () enthält Die Typen der IO-Funktionen erklären, warum man sie in der do-Notation (s.o.) gebrauchen kann Stefan Janssen Imperative Programmierung IO Monaden Main bei kompilierten Programmen per Standard wird bei einem kompilierten Programm die Funktion main in dem Module Main als Programmeinsprung verwendet: > module Main > where > main :: IO () Zugriff auf Kommandozeilenargumente: import System . Environment Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden getArgs :: IO [ String ] Exit-Status: data ExitCode = ExitSuccess | ExitFailure Int deriving ( Eq , Ord , Read , Show ) exitWith :: ExitCode -> IO a Main — btw Programmieren in Haskell Stefan Janssen Falls ein Modul keine module-Deklaration enthält, ist das äquivalent zu: 1 2 > module Main ( main ) > where Imperative Programmierung IO Monaden Convenience-IO-Funktionen Programmieren in Haskell Stefan Janssen 1 2 3 readFile :: FilePath -> IO String writeFile :: FilePath -> String -> IO () appendFile :: FilePath -> String -> IO () Imperative Programmierung IO Monaden Convenience-IO-Funktionen Programmieren in Haskell Stefan Janssen 1 2 3 readFile :: FilePath -> IO String writeFile :: FilePath -> String -> IO () appendFile :: FilePath -> String -> IO () Imperative Programmierung IO Monaden Sonstige IO-Funktionen: 1 2 openFile :: FilePath -> IOMode -> IO Handle ... Einfaches Beispiel Programmieren in Haskell Stefan Janssen 1 2 Datei lesen, verarbeiten, schreiben in Hugs > import System . IO > import Data . Char Imperative Programmierung IO 3 4 5 > gross :: String -> String > gross t = map toUpper t 6 7 8 9 > encode file = do > inp <- readFile ( file ) > writeFile ( file ++ " . enc " ) ( gross inp ) Monaden Stellenwert der do-Notation Programmieren in Haskell Die do-Notation ist überaus hilfreich für zustandsbasierte Programmierug, aber sie ist nur “syntaktischer Zucker”, also eine bequeme Schreibweise für Programme, die man auch ohne sie programmieren kann diese andere Programmierung erklärt die do-Notation in “normalem” Haskell, aber diese Erklärung der do-Notation muss man nicht kennen, um sie zu benutzen ... es sei denn, man will sie erweitern für den eigenen Bedarf Stefan Janssen Imperative Programmierung IO Monaden Imperativ funktional ... Programmieren in Haskell Stefan Janssen Wie bringt man die “Aktionen” auf “Zuständen” in einer funktionalen Welt unter? Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Imperativ funktional ... Programmieren in Haskell Stefan Janssen Wie bringt man die “Aktionen” auf “Zuständen” in einer funktionalen Welt unter? Funktionsanwendung z = f x y oder z = (f $ x ) $ y ersetzen durch Operation, die besagt Wende f auf s, x, und y an und produziere s’ und z Wende f’ auf s’, x, y und z an und produziere s” und z” und so weiter, ohne dass man die Folge s, s’, ... explizit hinschreiben muss. Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Monaden Programmieren in Haskell Monaden sind eine mathematisches Struktur, wie Gruppe, Ring, Körper, usw. dort untersucht in der der Kategorientheorie Hier: eine Klasse von Daten mit Operationen auf diesen, die bestimmte Eigenschaften besitzen Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Monaden Programmieren in Haskell Monaden sind eine mathematisches Struktur, wie Gruppe, Ring, Körper, usw. dort untersucht in der der Kategorientheorie Hier: eine Klasse von Daten mit Operationen auf diesen, die bestimmte Eigenschaften besitzen Grundidee: Statt Datentyp a verwendet man Typ M a Funktionsanwendung f a ersetzt durch a >>= f, direkte Anwendung f (M a) ist durch den Typ ausgeschlossen wobei >>= neben der Berechnung von f a weitere Geschäfte verrichten kann ... Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Besonderheit der Monaden Programmieren in Haskell Stefan Janssen Monaden sind nicht einfach zu verstehen, weil die Funktionsanwendung die grundlegendste Operation der funktionalen Programmierung ist, genau diese jetzt durch etwas (fast) Beliebiges ersetzt wird, das in jeder Monade anders ist und was wir auch selbst definieren können für neue Monaden Konkrete Monaden sind Typen, die Instanz der Typklasse Monad sind Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Funktionen auf Monaden Programmieren in Haskell 1 2 In Haskell-Notation: ( > >=) :: m a return :: a Stefan Janssen -> ( a -> m b ) -> m b -> m a IO 3 4 5 Imperative Programmierung ( > >) fail :: m a -> m b :: String -> m a -> m b Monaden Bedeutung der Do-Notation Maybe Listen a und b: beliebige Typen m a und m b: ihre Werte in Monade verpackt (>>=) und (>>) werden “bind” oder “bindto” ausgesprochen Monadenfunktionen 1 2 ( > >=) :: m a return :: a -> ( a -> m b ) -> m b -> m a 3 4 5 ( > >) :: m a -> m b fail :: String -> m a Was leistet >>=? -> m b Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen “Entpackt” a aus der Monade, wendet f darauf an, liefert “verpacktes” Ergebnis, das weiter verwendet werden kann in der Form x >>= f >>= g >>= h ... Das “Auspacken” geschieht bei jeder Monade auf eigene Art – und es kann auch direkt das Ergebnis erzeugen. Monadenfunktionen Programmieren in Haskell Stefan Janssen 1 2 ( > >=) :: m a return :: a -> ( a -> m b ) -> m b -> m a Imperative Programmierung 3 4 5 ( > >) :: m a -> m b fail :: String -> m a Was leistet return? -> m b IO Monaden Bedeutung der Do-Notation Maybe Listen Verpacken eines Werts a in der Monade, damit man mit >>= und >> darauf arbeiten kann Monadenfunktionen 1 2 ( > >=) :: m a return :: a -> ( a -> m b ) -> m b -> m a Programmieren in Haskell Stefan Janssen 3 4 5 ( > >) :: m a -> m b fail :: String -> m a Was leistet >>? -> m b Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe 1 Machmal möchte man eine Konstante für eine Funktion einsetzen: > count xs = sum ( map (\ x - >1) xs ) x >> f setzt x voraus, um f zu berechnen, also werden Ketten von Operationen sequenzialisiert durch x >> f1 >> f2 >> f3 ... Listen Monadenfunktionen 1 2 ( > >=) :: m a return :: a -> ( a -> m b ) -> m b -> m a Programmieren in Haskell Stefan Janssen 3 4 5 ( > >) :: m a -> m b fail :: String -> m a Was leistet >>? -> m b Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe 1 Machmal möchte man eine Konstante für eine Funktion einsetzen: > count xs = sum ( map (\ x - >1) xs ) x >> f setzt x voraus, um f zu berechnen, also werden Ketten von Operationen sequenzialisiert durch x >> f1 >> f2 >> f3 ... Beispiel: Sukzessives Schreiben von Output Listen Monadenfunktionen Programmieren in Haskell Stefan Janssen 1 2 ( > >=) :: m a return :: a -> ( a -> m b ) -> m b -> m a 3 4 5 Imperative Programmierung IO ( > >) :: m a -> m b fail :: String -> m a Was leistet fail? -> m b Erzeugen eines Fehler-Strings, mit dem die Rechnung abbricht Monaden Bedeutung der Do-Notation Maybe Listen Monaden als Instanzen Programmieren in Haskell Stefan Janssen Monaden sind eine Typklasse: Monad ihr gehören alle Typen an, auf denen man die Monadenfunktionen definiert hat Monad ist vordefiniert, vergleichbar anderen Typklassen wie Show, Eq, Ord, Num jeder konkrete Typ kann zur Instanz der Typklasse gemacht werden Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Typklasse Monad Programmieren in Haskell Stefan Janssen Vordefinierte Typklasse: (Achtung: m ist eine Variable für einen einstelligen Typ-Konstruktor!) 1 2 3 4 infixl 1 > >= , >> class Monad m where ( > >=) :: m a -> ( a -> m b ) -> m b return :: a -> m a Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe 5 Listen 6 7 ( > >) fail :: m a -> m b :: String -> m a -> m b Typklasse Monad Programmieren in Haskell Stefan Janssen Vordefinierte Typklasse: (Achtung: m ist eine Variable für einen einstelligen Typ-Konstruktor!) 1 2 3 4 infixl 1 > >= , >> class Monad m where ( > >=) :: m a -> ( a -> m b ) -> m b return :: a -> m a Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe 5 Listen 6 7 ( > >) fail :: m a -> m b :: String -> m a -> m b 6 7 8 m >> k fail s = m > >= \ _ -> k = error s -- default -- default Monad-Laws 1 return gibt nur sein Argument zurück und führt keine weiteren Berechnungen durch: m >>= return = m return x >>= f = f x 2 >>= ist assoziativ: (m >>= f) >>= g = m >>= (\x -> (f x >>= g)) analog zur Komposition von Funktionen (m . f ) . g = m . (f . g) Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Monad-Laws 1 return gibt nur sein Argument zurück und führt keine weiteren Berechnungen durch: m >>= return = m return x >>= f = f x 2 >>= ist assoziativ: (m >>= f) >>= g = m >>= (\x -> (f x >>= g)) analog zur Komposition von Funktionen (m . f ) . g = m . (f . g) Analog muss (>>) assoziativ sein: (a >> b) >> c = a >> (b >> c) fail muss die Berechnung abbrechen NB: Diese Eigenschaften werden von Monaden-Instanzen gefordert, können aber vom Compiler nicht überprüft werden! Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Beispiel: Maybe Monad 1 2 data Maybe a = Just a | Nothing Programmieren in Haskell Stefan Janssen 3 4 deriving ( Show , Eq ) Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Beispiel: Maybe Monad 1 2 data Maybe a = Just a | Nothing Programmieren in Haskell Stefan Janssen 3 4 deriving ( Show , Eq ) 5 6 7 8 9 instance Monad Maybe where ( Just x ) > >= k = k x Nothing > >= k = Nothing return x = Just x Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Beispiel: Maybe Monad 1 2 data Maybe a = Just a | Nothing 3 4 5 6 7 8 deriving ( Show , Eq ) Programmieren in Haskell Stefan Janssen Imperative Programmierung IO instance Monad Maybe where Monaden ( Just x ) > >= k = k x Nothing > >= k = Nothing return x = Just x Beispiele: Just 1 > >= \ x -> Just 2 > >= \ y -> return ( x + y ) ( ) Just 1 > >= \ x -> Nothing > >= \ y -> return ( x + y ) ( ) Bedeutung der Do-Notation Maybe 9 Listen Notation ...? Programmieren in Haskell Stefan Janssen Im wesentlichen hat man einen monadischen Wert m darauf angewandte Ketten von >>= und >> Operationen Das ist einfach, sieht aber nicht besonders übersichtlich aus ... Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation (Do-Expression) Syntaktischer Zucker, um >>= Ketten schöner hinschreiben zu können. Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation (Do-Expression) Syntaktischer Zucker, um >>= Ketten schöner hinschreiben zu können. m > >= f = do { x <- m ; f x } Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation (Do-Expression) Syntaktischer Zucker, um >>= Ketten schöner hinschreiben zu können. m > >= f = do { x <- m ; f x } bzw. m > >= f = do x <- m f x Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation (Do-Expression) Syntaktischer Zucker, um >>= Ketten schöner hinschreiben zu können. m > >= f = do { x <- m ; f x } bzw. m > >= f = do x <- m f x und m1 >> m2 = do _ <- m1 _ <- m2 Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation (Do-Expression) Syntaktischer Zucker, um >>= Ketten schöner hinschreiben zu können. m > >= f = do { x <- m ; f x } bzw. m > >= f = do x <- m f x und m1 >> m2 = do _ <- m1 _ <- m2 bzw. m1 >> m2 = do m1 m2 Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Do-Notation Programmieren in Haskell 1 2 Allgemeines Übersetzungsschema: m1 > >= \ x_1 -> m_2 > >= \ x_2 -> ... ... m_n > >= \ x_n -> return ... 5 6 7 8 9 10 Imperative Programmierung IO 3 4 Stefan Janssen = do Monaden x_1 <x_2 <x_3 <... x_n <return m_1 m_2 m_3 m_n ... Bedeutung der Do-Notation Maybe Listen Beobachtungen Programmieren in Haskell Stefan Janssen Imperative Programmierung IO >>= (bind-Operator) gibt eine Reihenfolge vor — Schachtelung ist beliebig (Assoziativität) Monaden Bedeutung der Do-Notation Maybe Listen Maybe-Beispiel Programmieren in Haskell 1 2 3 4 do x <- Just 1 y <- Just 2 return ( x + y ) Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Maybe-Beispiel Programmieren in Haskell 1 2 3 4 do x <- Just 1 y <- Just 2 return ( x + y ) Stefan Janssen Imperative Programmierung IO 1 2 Monaden Zum Ausprobieren in Hugs: 5 + do {x < - Just 1; y <- Just 2; return ( x + y )} Just 3 == do {x < - Just 1; y <- Just 2; return ( x + y )} Bedeutung der Do-Notation Maybe Listen Maybe-Beispiel Programmieren in Haskell 1 do x <- Just 1 y <- Just 2 return ( x + y ) 2 3 4 Stefan Janssen Imperative Programmierung IO 1 2 Monaden Zum Ausprobieren in Hugs: 5 + do {x < - Just 1; y <- Just 2; return ( x + y )} Just 3 == do {x < - Just 1; y <- Just 2; return ( x + y )} Bedeutung der Do-Notation Maybe Listen 5 6 7 8 9 do x <- Just 1 y <- Nothing return ( x + y ) Maybe-Beispiel 1 f dict = do 2 x <- lookup " alpha " dict y <- lookup " beta " dict z <- lookup " gamma " dict return ( x + y + z ) lookup :: String -> Dict -> Maybe Int 3 4 5 6 Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Maybe-Beispiel 1 f dict = do 2 x <- lookup " alpha " dict y <- lookup " beta " dict z <- lookup " gamma " dict return ( x + y + z ) lookup :: String -> Dict -> Maybe Int 3 4 5 6 Programmieren in Haskell Stefan Janssen Imperative Programmierung IO Monaden statt sowas wie: Bedeutung der Do-Notation Maybe Listen 6 7 8 9 10 11 12 f dict = if isJust x && isJust y && isJust z then Just ( fromJust x + fromJust y + fr else Nothing where x = lookup " alpha " dict y = lookup " beta " dict z = lookup " gamma " dict Monad-Laws in do-Notation Programmieren in Haskell Stefan Janssen 1 do { x <- m ; return x } = m 2 do { y <- return x ; f y 3 Assoziativität von >>=: do { y <- do { x <- m ; f x } ; g y } } = f x Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen = do { x <- m ; do { y <- f x ; g y } } = do { x <- m ; y <- f x ; g y } Do-Notation — let-Deklaration Programmieren in Haskell Stefan Janssen 1 2 3 Weiterer syntaktischer Zucker — statt let a = 42 in do print a Imperative Programmierung IO Monaden Bedeutung der Do-Notation 1 2 3 kann man auch schreiben: do let a = 42 print a Maybe Listen Listen-Beispiel Programmieren in Haskell Stefan Janssen 1 2 -- data [ a ] = a : [ a ] | -[] 5 6 IO Monaden 3 4 Imperative Programmierung instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Bedeutung der Do-Notation Maybe Listen Listen-Beispiel mit selbstdefinierten Datentyp Programmieren in Haskell Stefan Janssen 1 2 data List a = Cons a ( List a ) | Nil 5 6 IO Monaden 3 4 Imperative Programmierung instance Monad List where ( > >=) l f = concatList ( mapList f l ) return x = [x] Bedeutung der Do-Notation Maybe Listen Listen-Beispiel Programmieren in Haskell 1 2 -- data [ a ] = a : [ a ] | -[] 3 4 5 6 instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Listen-Beispiel Programmieren in Haskell 1 2 -- data [ a ] = a : [ a ] | -[] 3 4 5 6 instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] do x <- [1 ,2] y <- [3 ,4] return ( x * y ) Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Listen-Beispiel Programmieren in Haskell 1 2 -- data [ a ] = a : [ a ] | -[] 3 4 5 6 instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] do x <- [1 ,2] y <- [3 ,4] return ( x * y ) = > [3 ,4 ,6 ,8] Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Listen-Beispiel Programmieren in Haskell 1 2 -- data [ a ] = a : [ a ] | -[] 3 4 5 6 instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] do x <- [1 ,2] y <- [3 ,4] return ( x * y ) = > [3 ,4 ,6 ,8] Analog zu folgender Listenbeschreibung: [ x * y | x <- [1 ,2] , y <- [3 ,4] ] Stefan Janssen Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Beobachtungen zur do-Notation Programmieren in Haskell Stefan Janssen Imperative Programmierung sieht sehr imperativ aus IO „Zuweisungen“ zu Variablen Monaden → aber nur in Single-Assignment (SA) Form Bedeutung der Do-Notation Maybe Listen Zurück zum IO in Haskell Programmieren in Haskell Stefan Janssen mit der Monade IO IO als einstelliger Typkonstruktor IO als ADT importieren mit import System.IO Ausgabe mit Typ IO () Eingabe mit Typ IO String, IO Int, etc Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen Zurück zum IO in Haskell Programmieren in Haskell Stefan Janssen mit der Monade IO IO als einstelliger Typkonstruktor IO als ADT importieren mit import System.IO Ausgabe mit Typ IO () Eingabe mit Typ IO String, IO Int, etc → Monadeneigenschaft definiert die Reihenfolge IO-Fehler durch error Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen IO-Monad Beobachtungen Programmieren in Haskell Stefan Janssen Referential-Transparency? Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen IO-Monad Beobachtungen Programmieren in Haskell Stefan Janssen Referential-Transparency? → ja — nur sind die Werte von IO a verborgen (ADT-Eigenschaft) wie kommt der eigene Code z.B. an den Char-Wert bei IO String? Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen IO-Monad Beobachtungen Programmieren in Haskell Stefan Janssen Referential-Transparency? → ja — nur sind die Werte von IO a verborgen (ADT-Eigenschaft) wie kommt der eigene Code z.B. an den Char-Wert bei IO String? Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen → durch die Definition von >>= IO-Monad Beobachtungen Programmieren in Haskell Stefan Janssen Referential-Transparency? → ja — nur sind die Werte von IO a verborgen (ADT-Eigenschaft) wie kommt der eigene Code z.B. an den Char-Wert bei IO String? Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen → durch die Definition von >>= eindeutige Sequenzierung der IO-Funktionen durch Monadeneigenschaft Anwendungsgebiete Programmieren in Haskell Stefan Janssen Wichtigste Anwendung der Monaden Input / Output Fehlerbehandlung zustandbasierte Programme destructive updates (!!) Pssst: Verpackung des Zustands in einer Monade erlaubt dem Compiler speichereffiziente Implementierung durch destruktive Updates. Imperative Programmierung IO Monaden Bedeutung der Do-Notation Maybe Listen