Funktionale Programmierung 28.6.2005 1 Das Funktionale Quiz Was ist der Unterschied zwischen der Animation-Bibliothek und funktional-reaktiver Programmierung? "Reaktivität", d.h. Reaktion auf Ereignisse 2-3 Das Funktionale Quiz Warum werden die Operatoren ->> und =>> so häufig verwendet? untilB wartet auf Event und liefert neues Behaviour Neues Behaviours ist Wert des Ereignisses, ->> und =>> geben Events neue Werte untilB :: Beh a -> Ev (Beh a) -> Beh a (->>) :: Ev a-> b-> Ev b 4-5 Themen heute • Felder mit Labeln • Monaden kombinieren • Typinferenz 6 Beispiel: Felder mit Labeln Aus der Grafik-Bibliothek: data Event = Key {char :: Char, isDown :: Bool} | Button {pt :: Point, isLeft,isDown :: Bool} | MouseMove {pt :: Point} ... 7 Felder mit Labeln Argumente der Konstruktoren können Namen haben Syntax: Constr {name :: type,...} Semantik: wie vorher und zusätzlich: • Konstruktoraufruf mit Feldern: Constr {name = Wert,... Dabei Reihenfolge der Felder egal. Z.B. k = Key {isDown = False, char = 'a'} • Feldnamen sind Funktionen, die Felder extrahieren. Z.B. char k--> 'a' • Selektives Update, d.h. aus bestehendem Wert neuen erstellen und dabei einzelne Felder verändern. Z.B. k {isDown = True}--> Key 'a' True • Selektiv Variablen binden beim Pattern-Matching 8 Neue Allgemeine Syntax für algebraische Datentypen topdecl -> simpletype -> constrs -> constr -> | deriving -> data [context =>] simpletype = constrs [deriving] tycon tyvar1 ... tyvark constr1 | ... | constrn Con atype1 ... atypek Con { fielddecl1 , ... , fielddecln } deriving (dclass | (dclass1, ... , dclassn)) 9 Mehrere Monaden Eine Monade kapselt i.d.R. eine Sorte von Berechnungen Oftmals sind mehrere Sorten von Berechungen notwendig Möglichkeiten: • Monade definieren, die alles benötigte kann Nachteil: nicht modular • Monaden kombinieren 10 Monaden kombinieren Sehr aufwendig, wenn bestehende Monaden nicht verändert werden sollen Einfacher: Monaden definieren, die zu bestehender Basis-Monade weitere Berechnung hinzufügen "Monaden-Transformer" Idee: Berechnungen der Basis-Monade sind Ergebnisse des Transformers Operationen der Basis-Monade werden in die neue Monade geliftet 11 Monaden-Transformer für Maybe-Monade Berechungen ausführen, bis Fehler auftritt newtype MaybeT m a = MaybeT (m (Maybe a)) returnMT :: Monad m => a -> MaybeT m a returnMT v = MaybeT (return (Just v)) return gehört zur Basis-Monade 12 Einschub: $ ($) :: (a -> b) -> a -> b f $ a = f a Hilft Klammern sparen 13 >>= für MaybeT >>= der Basis-Monade ausführen, wenn noch kein Fehler aufgetreten ist bindMT :: Monad m => MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b (MaybeT x) `bindMT` f = MaybeT $ do r <- x case r of Just v -> let MaybeT x = f v in do r2 <- x return r2 Nothing -> return Nothing 14 Instanzdeklaration für MaybeT instance Monad m => Monad (MaybeT m) where return = returnMT (>>=) = bindMT fail s = MaybeT (return Nothing) 15 Liften der Basis-Operationen Nichts schlägt fehl: liftMaybe :: Monad m => m a -> MaybeT m a liftMaybe m = MaybeT (do v <- m return (Just v)) 16 Beispiel: MaybeT auf IO anwenden putStrLnMB :: String -> MaybeT IO () putStrLnMB s = liftMaybe (putStrLn s) s :: s = do putStrLnMB "eins" fail "Fehler!" putStrLnMB "ywei" runIOMB :: MaybeT IO a -> IO a runIOMB (MaybeT x) = x runIOMB s gibt nur "eins" aus 17 Transformer für Zustandstransformer newtype StateTT m s a = StateMonad (s -> m (a,s)) returnSTT :: Monad m => b -> StateTT m c b returnSTT v = StateMonad (\s -> return (v,s)) bindSTT :: Monad m => StateTT m b c -> (c -> StateTT m b d) -> StateTT m b d bindSTT (StateMonad x) f = StateMonad (\s -> do (v,s') <- x s let StateMonad g = f v in g s') return und >>= sind aus der Basis-Monade 18 Operationen für Zustandstransformer fetchST :: Monad m => StateTT m s s fetchST = StateMonad (\ s -> return (s,s)) storeST :: Monad m => v -> StateTT m v () storeST s = StateMonad (\ _ -> return ((),s)) return ist wieder aus Basis-Monade 19 Liften und laufen lasen für Zustandstransformer liftStateT :: Monad m => m a -> StateTT m s a liftStateT p = StateMonad $ \s -> do y <- p return (y,s) runStateT :: Monad m => StateTT m s a -> s -> m (a,s) runStateT (StateMonad f) = f 20 Beispiel: StateTT auf Roboter-Monade anwenden moveMemoT :: StateTT Robot a () moveMemoT = liftStateT move getPositionT :: StateTT Robot a Position getPositionT = liftStateT getPosition ... runMemoRobotT :: s -> MemoRobotT s () -> IO () runMemoRobotT init r = runRobot $ do (v,s) <- runStateT r init return () 21 move mit Zugriff auf State Zustand: Liste der besuchten Positionen moveMemoTtrail :: StateTT Robot [(Int,Int)] () moveMemoTtrail = do oldTrail <- fetchST pos <- getPositionT storeST (pos:oldTrail) moveMemoT 22 Alternatives move Zustand: Anzahl der Schritte moveMemoTtick :: StateTT Robot Int () moveMemoTtick = do oldTicks <- fetchST storeST (1 + oldTicks) moveMemoT 23 Roboter, der an schon bekanntem Ort dreht been = do trail <- fetchST pos <- getPositionT condT blockedT turnRightT moveMemoTtrail if pos `elem` trail then turnRightT else moveMemoTtrail been 24 Probleme Manche Monaden interagieren in nicht-trivialer Weise • MaybeT (StateT s a) liefert im Fehlerfall Store bei Abbruch • StateTT Maybe a s liefert im Fehlerfall keinen Store Transformer für Listen-Monade verlangt, dass Argument kommutativ ist 25 Roboter-Simulator ohne Transformer Zustandsmonade entspricht impliziten Parameteren tremaux :: Robot () tremaux = penDown >> (runTremaux []) where runTremaux :: Trail -> Robot() runTremaux trail = while (isnt aimReached) (do pos <- getPosition newTrail <- moveTremaux trail pos runTremaux newTrail) moveTremaux :: Trail -> Position -> Robot Trail 26 Typinferenz für parametrische Polymorphie Typinferenz: Typen eines Programms herleiten Sprache: λ-Kalkül mit let und Rekursion Also keine Typklassen, algebraischen Datentypen,... Literatur: Thiemann, 15.4 und 15.5.1 27