import Test.QuickCheck import Control.Monad import List -- Bool eqBool :: Bool -> Bool -> Bool eqBool False False = True eqBool False True = False eqBool True False = False eqBool True True = True showBool :: Bool -> String showBool False = "False" showBool True = "True" -- Season data Season = Winter | Spring | Summer | Fall deriving (Eq) next :: Season -> Season next Winter = Spring next Spring = Summer next Summer = Fall next Fall = Winter eqSeason :: Season -> Season -> Bool eqSeason Winter Winter = True eqSeason Spring Spring = True eqSeason Summer Summer = True eqSeason Fall Fall = True eqSeason x y = False showSeason :: Season -> String showSeason Winter = "Winter" showSeason Spring = "Spring" showSeason Summer = "Summer" showSeason Fall = "Fall" toInt :: Season -> Int toInt Winter = 0 toInt Spring = 1 toInt Summer = 2 toInt Fall = 3 fromInt :: Int -> Season fromInt 0 = Winter fromInt 1 = Spring fromInt 2 = Summer fromInt 3 = Fall next' :: Season -> Season next' x = fromInt ((toInt x + 1) `mod` 4) prop_next :: Season -> Bool prop_next x = next x == next' x eqSeason' :: Season -> Season -> Bool eqSeason' x y = (toInt x == toInt y) prop_eqSeason :: Season -> Season -> Bool prop_eqSeason x y = eqSeason x y == eqSeason' x y -- Shape type Radius = Float type Width = Float type Height = Float data Shape = Circle Radius | Rect Width Height deriving (Eq,Show) area :: Shape -> Float area (Circle r) = pi * r^2 area (Rect w h) = w * h eqShape :: Shape -> Shape -> Bool eqShape (Circle r) (Circle r') = (r == r') eqShape (Rect w h) (Rect w' h') = (w == w') && (h == h') eqShape x y = False showShape :: Shape -> String showShape (Circle r) = "Circle " ++ showF r showShape (Rect w h) = "Rect " ++ showF w ++ " " ++ showF h showF :: Float -> String showF x | x >= 0 = show x | otherwise = "(" ++ show x ++ ")" prop_eqShape :: Shape -> Shape -> Bool prop_eqShape x y = eqShape x y == (x == y) prop_showShape :: Shape -> Bool prop_showShape x = showShape x == show x data List a = Nil | Cons a (List a) append :: List a -> List a -> List a append Nil ys = ys append (Cons x xs) ys = Cons x (append xs ys) fromList :: List a -> [a] fromList Nil = [] fromList (Cons x xs) = x : fromList xs toList :: [a] -> List a toList [] = Nil toList (x:xs) = Cons x (toList xs) prop_list :: [Int] -> Bool prop_list xs = fromList (toList xs) == xs prop_append :: [Int] -> [Int] -> Bool prop_append xs ys = fromList (append (toList xs) (toList ys)) == xs ++ ys -- Expressions data Exp = Lit Int | Add Exp Exp | Mul Exp Exp evalExp :: Exp -> Int evalExp (Lit n) = n evalExp (Add e f) = evalExp e + evalExp f evalExp (Mul e f) = evalExp e * evalExp f showExp :: Exp -> String showExp (Lit n) = show n showExp (Add e f) = par (showExp e ++ "+" ++ showExp f) showExp (Mul e f) = par (showExp e ++ "*" ++ showExp f) par :: String -> String par s = "(" ++ s ++ ")" e0, e1 :: Exp e0 = Add (Lit 2) (Mul (Lit 3) (Lit 3)) e1 = Mul (Add (Lit 2) (Lit 3)) (Lit 3) test_Exp :: Bool test_Exp = showExp e0 == "(2+(3*3))" && evalExp e0 == 11 && showExp e1 == "((2+3)*3)" && evalExp e1 == 15 -- Propositions type Name = String data Prp = Var Name | F | T | Not Prp | Prp :|: Prp | Prp :&: Prp deriving (Eq, Ord, Show) type Names = [Name] type Env = [(Name,Bool)] showPrp :: Prp -> String showPrp (Var x) = x showPrp (F) = "F" showPrp (T) = "T" showPrp (Not p) = par ("~" ++ showPrp p) showPrp (p :|: q) = par (showPrp p ++ "|" ++ showPrp q) showPrp (p :&: q) = par (showPrp p ++ "&" ++ showPrp q) names :: Prp -> Names names (Var x) = [x] names (F) = [] names (T) = [] names (Not p) = names p names (p :|: q) = nub (names p ++ names q) names (p :&: q) = nub (names p ++ names q) eval :: Env -> Prp -> Bool eval e (Var x) = lookUp e x eval e (F) = False eval e (T) = True eval e (Not p) = not (eval e p) eval e (p :|: q) = eval e p || eval e q eval e (p :&: q) = eval e p && eval e q lookUp :: Eq a => [(a,b)] -> a -> b lookUp xys x = the [ y | (x',y) <- xys, x == x' ] where the [x] = x p0 :: Prp p0 = (Var "a" :&: Var "b") :|: (Not (Var "a") :&: Not (Var "b")) p1 :: Prp p1 = (Var "a" :&: Not (Var "a")) env0 :: Env env0 = [("a",False), ("b",False)] test_Prp :: Bool test_Prp = showPrp p0 == "((a&b)|((~a)&(~b)))" && showPrp p1 == "(a&(~a))" && names p0 == ["a","b"] && eval env0 p0 == True && lookUp env0 "a" == False envs :: Names -> [Env] envs [] = [[]] envs (x:xs) = [ (x,b):e | b <- bs, e <- envs xs ] where bs = [False,True] test_envs :: Bool test_envs = envs [] == [[]] && envs ["b"] == [[("b",False)], [("b",True )]] && envs ["a","b"] == [[("a",False),("b",False)], [("a",False),("b",True )], [("a",True ),("b",False)], [("a",True ),("b",True )]] satisfiable :: Prp -> Bool satisfiable p = or [ eval e p | e <- envs (names p) ] test_satisfiable :: Bool test_satisfiable = satisfiable p0 && not (satisfiable p1) -- Simplify isSimple :: Prp -> Bool isSimple (Not p) = isSimple p && not (isOp p) where isOp (Not p) = True isOp (p :|: q) = True isOp (p :&: q) = True isOp p = False isSimple (p :|: q) = isSimple p && isSimple q isSimple (p :&: q) = isSimple p && isSimple q isSimple p = True simplify :: Prp -> Prp simplify (Not p) = knot (simplify p) where knot (Not p) = p knot (p :|: q) = knot p :&: knot q knot (p :&: q) = knot p :|: knot q knot p = Not p simplify (p :|: q) = simplify p :|: simplify q simplify (p :&: q) = simplify p :&: simplify q simplify p = p p2 :: Prp p2 = Not p0 test_simplify :: Bool test_simplify = showPrp p2 == "(~((a&b)|((~a)&(~b))))" && showPrp (simplify p2) == "(((~a)|(~b))&(a|b))" && not (isSimple p2) && isSimple (simplify p2) -- allow QuickCheck to generate arbitrary values of type Prp instance Arbitrary Prp where arbitrary = sized prp where prp 0 = oneof [return F, return T, liftM Var arbitrary] prp n | n > 0 = oneof [return F, return T, liftM Var arbitrary, liftM Not (prp (n-1)), liftM2 (:&:) (prp (n `div` 2)) (prp (n `div` 2)), liftM2 (:|:) (prp (n `div` 2)) (prp (n `div` 2))] prop_simplify :: Prp -> Bool prop_simplify p = isSimple (simplify p) prop_eval_simplify :: Prp -> Bool prop_eval_simplify p = and [ eval e p == eval e (simplify p) | e <- envs (names p) ] -- All sublists of a list subs :: [a] -> [[a]] subs [] = [[]] subs (x:xs) = subs xs ++ [ x:ys | ys <- subs xs ] test_subs :: Bool test_subs = subs [] == [[] :: [String]] && subs ["b"] == [[], ["b"]] && subs ["a","b"] == [[], ["b"], ["a"], ["a","b"]] -- Micro-Haskell data Univ = UBool Bool | UInt Int | UList [Univ] | UFun (Univ -> Univ) data Hask = HTrue | HFalse | HIf Hask Hask Hask | HLit Int | HEq Hask Hask | HAdd Hask Hask | HVar Name | HLam Name Hask | HApp Hask Hask type HEnv = [(Name, Univ)] showUniv :: Univ -> String showUniv (UBool b) = show b showUniv (UInt i) = show i showUniv (UList us) = "[" ++ concat (intersperse "," (map showUniv us)) ++ "]" eqUniv :: Univ -> Univ -> Bool eqUniv (UBool b) (UBool c) = b == c eqUniv (UInt i) (UInt j) = i == j eqUniv (UList us) (UList vs) = and [ eqUniv u v | (u,v) <- zip us vs ] eqUniv u v = False hEval :: Hask -> HEnv -> Univ hEval HTrue r = UBool True hEval HFalse r = UBool False hEval (HIf c d e) r = hif (hEval c r) (hEval d r) (hEval e r) where hif (UBool b) v w = if b then v else w hEval (HLit i) r = UInt i hEval (HEq d e) r = heq (hEval d r) (hEval e r) where heq (UInt i) (UInt j) = UBool (i == j) hEval (HAdd d e) r = hadd (hEval d r) (hEval e r) where hadd (UInt i) (UInt j) = UInt (i + j) hEval (HVar x) r = lookUp r x hEval (HLam x e) r = UFun (\v -> hEval e ((x,v):r)) hEval (HApp d e) r = happ (hEval d r) (hEval e r) where happ (UFun f) v = f v h0 = (HApp (HApp (HLam "x" (HLam "y" (HAdd (HVar "x") (HVar "y")))) (HLit 3)) (HLit 4)) test_h0 = eqUniv (hEval h0 []) (UInt 7)