Deklarative Programmierung

Werbung
Was bisher geschah
I
Algebraische Datentypen
I
Pattern Matching
I
Funktionen höherer Ordnung
I
Rekursionsschemata map, fold
über Zahlen, Listen, Bäumen, ...
I
strukturelle Induktion
I
Polymorphie, Typklassen
I
Lazy evaluation
I
Streams, Musik
83
Funktionen mit Zusatzinformation
Funktion
f :: Int -> Int
erweitern um zusätzliche Information (String)
f’ :: Int -> (Int, String)
Verkettung zweier Funktionen
f,g :: Int -> Int
(f . g) x = f (g x)
beide mit zusätzlicher Information
f’, g’ :: Int -> (Int, String)
Nacheinanderausführung
let ( y, s ) = g’ x
( z, s’) = f’ y
in (z, s ++ "," ++ s’)
umständlich, besser wäre
Kombination (Nacheinanderausführung) mit Verknüpfung der
Informationen
84
Transformation: Wert → Wert mit Zusatzinformation
infos x = (x, show x)
Transformation: Funktion → Funktion mit Zusatzinformation
lift f x = (f x, "")
alternative Defintion für lift
lift f = infos . f
Es gilt also
f’ = lift f , g’ = lift g
lift lässt sich also durch infos ausdrücken
85
Nacheinanderausführung
Definition:
nach :: (a -> (b, String) ) -> (a, String)
-> (b, String)
nach f’ (y,s) = let (z,s’) = f’ y
in (z, s ++ "." ++ s’)
Verwendung:
g’ ‘nach‘ ( f’ ‘nach‘ (infos 4)))
oder
( (lift g) ‘nach‘ ( ( lift f) ‘nach‘ (infos 4)))
mon_info.hs
86
Datentyp für Wert mit Zusatzinformation
f, g :: a -> b
data MZ a = MZ (a, String)
f’, g’ :: a -> MZ b
Nacheinanderausführung
let MZ ( y, s ) = g’ x
MZ ( z, s’) = f’ y
in MZ (z, s ++ "." ++ s’)
Kombination (Nacheinanderausführung) mit Verknüpfung der
Informationen
Definition:
nach :: (a -> MZ b) -> MZ a -> MZ b
nach f (MZ (y,s)) = let (MZ (z,s’)) = f y
in MZ (z, s ++ "." ++ s’)
mon_mz.hs
87
Maybe
data Maybe a = Just a | Nothing
f’ : a -> Maybe b
g’ : b -> Maybe c
Nacheinanderausführung
case ( g’ x ) of
Nothing -> Nothing
Just y -> f’ y
umständlich, besser wäre
Kombination (Berechnung) mit automatischer Übergabe von
Nothing
88
Fehlermitteilung
data MayFail a = Error EC | Result a
data EC = Fgross | Gklein
f’ : a -> MayFail b
g’ : b -> MayFail c
Nacheinanderausführung
case ( g’ x ) of
Error c -> Error c
Result y -> f’ y
umständlich, besser wäre
Kombination (Berechnung) mit automatischer Übergabe der
Fehlermitteilung
89
Gemeinsames Muster
f :: a -> b
g :: b -> c
Übergang zu erweitertem Ergebnistyp
f’ :: a -> .. b
g’ :: b -> .. c
Nacheinanderausführung solcher Funktionen mit
automatischer Übergabe
nach :: (a -> .. b) -> .. a -> .. b
90
in den Beispielen
I
Zusatzinformation
data MZ a = MZ ( a, String
nach :: (Int -> MZ Int) ->
nach f (MZ (y,s)) = let MZ
in MZ (z, s
I
)
MZ Int -> MZ Int
(z,s’) = f y
++ "." ++ s’)
Maybe
nach :: (Int -> Maybe Int) -> Maybe Int
-> Maybe Int
nach f y = case y of
Nothing -> Nothing
Just y -> f y
I
Fehlermitteilung
nach :: (Int -> MayFail Int) -> MayFail Int
-> MayFail Int
nach f y = case y of
Error c -> Error c
Result y -> f y
91
Monaden
Ziel: Verknüpfung von Berechnungen zu komplexeren Berechnungen
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
mit den Operationen:
return (Eingang)
return :: a -> m a
bind (sequentielle Verknüpfung)
(>>=) :: m a -> (a -> m b) -> m b
Monaden-Gesetze:
I
( ( return x ) >>= f ) = (f x)
I
( m >>= return ) = m
I
((m >>= f) >>= g) = (m >>= (\ x -> f x >>= g))
(man bemerke die Analogie zu Monoid-Gesetzen)
92
Beispiel Zusatzinformation
data MZ a = MZ (a, String)
instance Monad MZ where
-- return :: Int -> MZ Int
return x = (x, "")
-- (>>=) :: MZ Int -> (Int -> MZ Int) -> MZ Int
(y,s) >>= f = let (z,s’) = f y
in (z, s ++ "." ++ s’)
93
Maybe-Monade
data Maybe a = Just a | Nothing
statt:
case ( evaluate e l ) of
Nothing -> Nothing
Just a -> case ( evaluate e r ) of
Nothing -> Nothing
Just b -> Just ( a + b )
mit Hilfe der Maybe-Monade
instance Monad Maybe where
return = \ x -> Just x
m >>= f = case m of
Nothing -> Nothing
Just x -> f x
kürzer und übersichtlicher:
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
94
MayFail-Monade
data MayFail a = Error EC | Result a
data EC = Fgross | Gklein
statt:
case ( evaluate e l ) of
Error c -> Error c
Result a -> case ( evaluate e r ) of
Error c -> Error c
Result b -> Result ( a + b )
mit Hilfe der MayFail-Monade
instance Monad MayFail where
return = \ x -> Result x
m >>= f = case m of
Error c -> Error c
Result b -> f b
kürzer und übersichtlicher:
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
95
Listen-Monade
instance Monad [a] where
return = \ x -> [x]
m >>= f = concat ( map f m )
Beispiel: Kreuzprodukt von xs :: [a] mit ys :: [b]
cross xs ys =
concat ( map ( \ x ->
concat ( map ( \ y ->
[ (x,y) ] ) ys ) ) xs )
besser:
cross xs ys =
xs >>= \ x ->
ys >>= \ y ->
return (x,y)
noch einfacher lesbar:
cross xs ys = do x <- xs
y <- ys
return (x,y)
96
Do-Notation für Monaden
Beispiel: original
evaluate e l >>= \ a ->
evaluate e r >>= \ b ->
return ( a + b )
do-Notation
do { a <- evaluate e l
; b <- evaluate e r
; return ( a + b )
}
do-Notation (implizit geklammert)
do a <- evaluate e l
b <- evaluate e r
return ( a + b )
Übersetzung:
I
do f übersetzt zu f
I
do f; ... übersetzt zu f >>= \ _ -> do
I
do x <- f; ... übersetzt zu f >>= \ x -> do
97
Berechnungen mit Nebenwirkungen
Nebenwirkungen sind mitunter erwünscht, z.B.
I
von-Neumann-Maschine: Änderungen im Hauptspeicher
I
Modellierung von Prozessen mit Zustandsübergängen
Zustände haben Typ State
Übergang von Val zu
State -> (State, Val)
98
Zustands-Monade
data State s a = ST ( s -> (a,s) )
instance Monad State where
-- return :: a -> State s a
return x = ST (\x -> (x,s))
-- (>>=) :: State s a -> (a -> State s b) -> State s b
(ST m) >>= f = ST (\s -> let (x,s1) = m s
ST f’ = f x
in f’ s1)
Verwendung:
tick :: State Int ()
tick = ST ( \ s -> ((), s+1 ))
displayState :: Show a => State Int a -> String
displayState m = let (a,s) =(f m 0)
in show s ++ " " ++ show a
displayState $ tick >> tick >> tick
displayState ( do tick ; tick )
99
Interaktive Programme
I
Berechnungen in Haskell sind nebenwirkungsfrei.
(Diese Eigenschaft soweit wie möglich beibehalten)
I
Ein- und Ausgabe sind Nebenwirkungen.
(Nebenwirkungen zulassen, wo sie unvermeidbar sind)
I
Bei Ein- und Ausgabeaktionen ist die
Ausführungsreihenfolge wichtig.
Idee:
I
Isolation der Programmteile mit Nebenwirkungen
(Operation return )
I
sequentielle Verknüpfung von Ausdrücken mit
Nebenwirkungen
(Operation bind >>= )
100
IO-Monade
class Monad m where
return :: a -> m a
( >>= ) :: m a -> (a -> m b) -> m b
für m = IO:
return :: a -> IO a
( >>= ) :: IO a -> (a -> IO b) -> IO b
instance Monad IO where
return v = \w -> (v, w)
f >>= g = \w -> case f w of
(v, w’) -> g v w’
101
Abstraktes Modell für IO
I
IO a = Aktion mit Resultat :: a und Nebenwirkung
I
ein ausführbares Haskell-Programm enthält
module Main where
main :: IO ()
main = ...
diese Aktion wird (berechnet und) ausgeführt.
oft zusätzlich notwendig:
import System.IO
main = do
hSetBuffering stdout NoBuffering
...
102
Konkretes Modell für IO: Zustand
Typ für Aktionen, Änderung des Weltzustandes
data World = ...
data IO = World -> World
data IO a = IO { World -> (a, World) }
das Welt-Objekt bezeichnet die Welt außerhalb des
Programmes
Problem:
f :: World -> ( World, World )
f w = ( deleteFile "foo" w, putStr "bar" w )
mögliche Lösungen in verschiedenen Sprachen:
I
Haskell: Typ World ist privat, öffentlich ist nur IO
I
Clean: Typ World ist öffentlich, aber unique
103
Herunterladen