Programmieren in Haskell

Werbung
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
Herunterladen