module Main where -- Erweitere einfachen Auswerter arithmetischer Ausdruecke um -- 1. Fehlerbehandlung -- 2. Zaehler der durchgefuehrten Operationen (Zustand) -- 3. Ausgabeprotokoll der Operationen -- Ausgangsauswerter data Expr = Con Int | Bin Op Expr Expr deriving Show data Op = Add | Sub | Mul | Div deriving (Show,Eq) eval :: Expr -> Int eval (Con a) = a eval (Bin op a b) = sem op (eval a) (eval b) sem :: Op -> (Int -> Int -> Int) sem Add = (+) sem Sub = (-) sem Mul = (*) sem Div = div success, failure :: Expr success = Bin Sub (Bin Mul (Con 12) (Con 4)) (Bin Add (Con 1) (Con 5)) failure = Bin Div (Con 1) (Con 0) -- Erweiterung um Fehlerbehandlung data Result a = Raise Exception | Return a deriving Show type Exception = String evalF :: Expr -> Result Int evalF (Con a) = Return a evalF (Bin op a b) = case evalF a of Raise e -> Raise e Return n -> case evalF b of Raise e -> Raise e Return m -> if op == Div && m == 0 then Raise "Divide by zero" else Return (sem op n m) -- Erweiterung um Zählerzustand type Count a = Int -> (a, Int) evalC :: Expr -> Count Int evalC (Con a) x = (a,x) evalC (Bin op a b) x = let (n,y) = evalC a x (m,z) = evalC b y in (sem op n m, z+1) -- Erweiterung um Ausgabe type Output a = (a, String) evalW :: Expr -> Output Int evalW e@(Con a) = (a,write e a) evalW e@(Bin op a b) = let (n,as) = evalW a (m,bs) = evalW b r = sem op n m in (r, as ++ bs ++ write e r) write :: Expr -> Int -> String write e n = "eval (" ++ show e ++ ") => " ++ show n ++ "\n" -- >>>>> jeweils starke Modifikationen erforderlich -- Alternativer Ansatz: -- --------------------- -- Monadischer Auswerter -- --------------------- evalM :: Expr -> Id Int evalM (Con a) = return a evalM (Bin op a b) = evalM a >>= \ n -> evalM b >>= \ m -> return (sem op n m) -- Identitaetsmonade newtype Id a = Id a deriving Show instance Monad Id where return a = Id a (Id a) >>= f = f a -- Fehlerbehandlungsmonade instance Monad Result where return a = Return a a >>= f = case a of Raise e -> Raise e Return n -> f n -- .... und entsprechend erweiterter Auswerter evalFM :: Expr -> Result Int evalFM (Con a) = return a evalFM (Bin op a b) = evalFM a >>= \ n -> evalFM b >>= \ m -> if m == 0 && op == Div then Raise "Division durch Null" else return (sem op n m) -- Zustandsmonade newtype CountM a = Count (Int -> (a, Int)) apply :: CountM a -> Int -> (a, Int) apply (Count f) i = f i instance Monad CountM where return a = Count (\ i -> (a,i)) g >>= f = Count (\ i -> let (n,j) = apply g i in apply (f n) j) -- .... und entsprechend erweiterter Auswerter evalCM :: Expr -> CountM Int evalCM (Con a) = return a evalCM (Bin op a b) = evalCM a >>= \ n -> evalCM b >>= \ m -> incr >> return (sem op n m) where incr :: CountM () incr = Count (\ i -> ((),i+1)) -- Ausgabemonade newtype OutputM a = Output (a, String) deriving Show pair :: OutputM a -> (a, String) pair (Output p) = p instance Monad OutputM where return a = Output (a, "") a >>= f = let (n, cs) = pair a (m, cs2) = pair (f n) in Output (m, cs ++ cs2) -- .... und entsprechend erweiterter Auswerter evalWM :: Expr -> OutputM Int evalWM e@(Con a) = output (write e a) >> return a evalWM e@(Bin op a b) = evalWM a >>= \ n -> evalWM b >>= \ m -> let res = sem op n m in output (write e res) >> return res output :: String -> OutputM () output cs = Output ((),cs) -- Listen- und Maybe-Monade {- -- Listenmonade instance Monad [] where return :: a -> [a] (>>=) :: [a] -> (a -> [b]) -> [b] return x = [x] xs >>= f = concatMap f xs -- Maybe-Monade instance Monad Maybe where return :: a -> Maybe a (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b return x = Just x (Just x) >>= f = f x Nothing >>= f = Nothing -} -- Beispiele -- Kartesiche Produktbildung cartesian :: Monad m => m a -> m b -> m (a,b) cartesian xs ys = do x <- xs; y <- ys; return (x,y) -- Constraint Solving -- Bestimme zu positiver ganzer Zahl alle Paare positiver ganzer Zahlen, -- deren Produkt die Zahl ergibt multiplyTo :: Int -> [(Int,Int)] multiplyTo n = do x <- [1..n] y <- [x..n] if x*y == n then return (x,y) else []