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