280 likes | 394 Views
Lecture #11, Nov. 01, 2004. Special Guest lecture by Tom Harke Today’s Topics The Haskell Class system Instance declarations Inheritance and dependent classes Derived instances The Show class The Fix operator from homework # 5 Reading Assignment Chapter 12 - Qualified Types
E N D
Lecture #11, Nov. 01, 2004 • Special Guest lecture by Tom Harke • Today’s Topics • The Haskell Class system • Instance declarations • Inheritance and dependent classes • Derived instances • The Show class • The Fix operator from homework # 5 • Reading Assignment • Chapter 12 - Qualified Types • Appendix - A Tour of Haskell’s Standard Type Classes Sections 24.4 & 24.5 The Show and Read Class (pp 334-340) • Reminder - Mid-Term Exam Wednesday - next class meeting! • open book exam
The Haskell Class System • Think of a Qualified type as a type with a Predicate • Types which meet those predicates have "extra" functionality. • A class definition defines the type of the "extra" functionality. • An instance declarations defines the "extra" functionality for a particular type.
Example Class Definition class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x==y) class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a Note default definition of (/=)
Properties of a class definition class (Eqa) => Orda where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a • Class name is capitalized, think of this as the name of a type predicate that qualifies the type being described. • Classes can depend on another class or in other words require another classes as a prerequisite • The methods of a class are functions whose type can depend upon the type being qualified • There can be more than one method. • The methods can be ordinary (prefix) functions or infix operators.
First Example • Example: • The Class Eq • Concrete Types without arrows (functions -> ) support equality. • Assume basic equalities: int_eq, string_eq, bool_eq • Make the following tree like data types that represent arithmetic expressions, instances of class Eq data Aop = Add | Sub | Mult | Div data Aexp = Num Int | Exp (Aexp, Aop, Aexp)
First define equality functions aop_eq Add Add = True aop_eq Sub Sub = True aop_eq Mult Mult = True aop_eq Div Div = True aop_eq _ _ = False aexp_eq (Num x) (Num y) = int_eq x y aexp_eq (Exp(e1,x,e2))(Exp(f1,y,f2)) = (aexp_eq e1 f1) && (aop_eq x y) && (aexp_eq e2 f2) aexp_eq _ _ = False
Then make Instance Definitions • To make an instance instance Eq(Aop) where x == y = aop_eq x y x /= y = not(aop_eq x y ) instance Eq(Aexp) where x == y = aexp_eq x y x /= y = not(aexp_eq x y ) • Example use ? (Num 3) == (Num 5) False ? (Num 3) == (Num (5-2)) True
Another Example: Num Complex • Make Complex numbers an instance of class Num. data Complex = C Float Float • An instance of Num, must first be an instance of Eq and Show and provide methods for (+), (-), and (*) (amongst others). First provide the numeric operators complex_add (C x y) (C a b) = C (x+a) (y+b) complex_sub (C x y) (C a b) = C (x-a) (y-b) complex_mult (C x y) (C a b) = C (x*a - y*b) (x*b + a*y)
Num Instance Then make the instance declarations instance Eq(Complex) where (C x y) == (C a b) = x==a && y==b instance Show(Complex) where showsPrec = error "No show for complex" showList = error "No show for complex" instance Num(Complex) where x + y = complex_add x y x - y = complex_sub x y x * y = complex_mult x y • Note that the Show instance is quite imprecise, but this will cause an error only if it is ever used
Full Num Class class (Eq a, Show a) => Num a where (+), (-), (*) :: a -> a -> a negate :: a -> a abs, signum :: a -> a fromInteger :: Integer -> a fromInt :: Int -> a x - y = x + negate y fromInt = fromIntegral
Class Ord • The Class Ord is interesting for several reasons • It’s a dependant Class, so it uses inheritance Class Eq a => Ord a where ... • It makes extensive use of default method definitions • Example: Eq Tree => Ord Tree data Tree a = Leaf a | Branch (Tree a) (Tree a) • First make Tree an instance of Eq instance Eq a => Eq (Tree a) where (Leaf x) == (Leaf y) = x==y (Branch x y) == (Branch a b) = x==a && y==b _ == _ = False
Make Tree an instance of Ord • First define a (<) and (<=) function for Tree Int instance (Ord a,Eq a) => Ord(Tree a) where (Leaf _) < (Branch _ _) = True (Leaf x) < (Leaf y) = x < y (Branch _ _) < (Leaf _) = False (Branch l1 r1) < (Branch l2 r2) = if l1==l2 then r1 < r2 else l1 < l2 t1 <= t2 = t1 < t2 || t1 == t2 is Eq a really necessary as a dependent type?
Full Definition of Ord class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a compare x y | x==y = EQ | x<=y = LT | otherwise = GT x <= y = compare x y /= GT x < y = compare x y == LT x >= y = compare x y /= LT x > y = compare x y == GT max x y | x >= y = x | otherwise = y min x y | x <= y = x | otherwise = y An instance need only provide enough methods to define the others. Here compare would be enough or just <= plus Eq a
A closer look. Classes for Type Constructors • How do classes work for type constructors like Tree and [-] • Consider: data Mylist a = Nil | Cons a (Mylist a) int_list_eq(Nil, Nil) = True int_list_eq(Cons x xs, Cons y ys) = (int_eq x y) && (int_list_eq(xs,ys)) int_list_eq (_, _) = False list_eq :: (a -> a -> Bool) -> (Mylist a,Mylist a) -> Bool list_eq f (Nil, Nil) = True list_eq f (Cons x xs, Cons y ys) = (f x y) && (list_eq f (xs, ys)) list_eq f (_, _) = False
Classes for Type Constructors • The function list_eq motivates the following Dependent instance. instance Eq a => Eq(Mylist a) where Nil == Nil = True (Cons x xs) == (Cons y ys) = (x == y) && (xs == ys) _ == _ = False ? Nil == (Cons 2 Nil) False ? (Cons 2 Nil) == (Cons (3-1) Nil) True What type is (==) used at? What type is (==) used at?
Think about this! • Consider : data Bush a = One a | Two (Bush a) (Bush a) | Many [Bush a] instance Eq a => Eq(Bush a) where (One x) == (One y) = (x == y) (Two x1 x2) == (Two y1 y2) = (x1 == y1) && (x2 == y2) (Many xs) == (Many ys) = (xs == ys) _ == _ = False ? Many [One 2] == Many [One 2, Two (One 3) (One 5)] False ? Many [One 3] == Many [One 3] True
Analyze instance Eq a => Eq(Bush a) where (One x) == (One y) = (x == y) (Two x1 x2) == (Two y1 y2) = (x1 == y1) && (x2 == y2) (Many xs) == (Many ys) = (xs == ys) _ == _ = False • (==) :: a -> a -> Bool • (==) :: Bush a -> Bush a -> Bool • (==) :: Bush a -> Bush a -> Bool • (==) :: [ Bush a ] -> [ Bush a ] -> Bool • How do we get (==) ?
The Class Show • Instances of the Class Show are those types that can be converted to character strings show :: Show a => a -> String • Example uses: show (2+2) ---> “4” show (not (3=4)) ---> “True” Using show and concatenation (++) “My name is” ++ show name ++ “I am” ++ show age ++ “years old” • (++) is right associative so this runs in linear time
Show and Trees • If we try and define a show function for trees we lose this linear time property. Consider a simplification of Class Show (see the appendix for the real definition): Instance Show a => Show (Tree a) where show (Leaf x) = “(Leaf ” ++ show x ++ “)” show (Branch x y) = “(Branch” ++ show x ++ show y ++ “)” When we Show the left sub-tree x, and the right sub-tree y, we end re-concatenating the string returned by the “recursive” call.
Restoring Linearity • Suppose we had a function shows shows :: Show a => a -> String -> String • And shows was part of the Show Class as well. Class Show a where show :: a -> String shows :: a -> String -> String • Where shows has an extra String argument • Think of this argument as an accumulating parameter, that holds all of the string printed so far. showsTree :: Show a => Tree a -> String -> String showsTree (Leaf x) s = “(Leaf ” ++ show x ++ “)” ++ s showsTree (Branch x y) s = “(Branch” ++ (showsTree x (showsTree y (“)” ++ s)))
show can be defined in terms of shows • showTree tree = showsTree tree “” • Exercise: write the function showsList :: Show a => [a] -> String -> String showsList [] s = ... showsList (x:xs) s = ...
Derived Instances • Some Classes are so useful and so simple to define, that we’d like the compiler to do it for us. • Example derivable Classes: • Eq • Ord • Show • Example Uses of deriving classes data Color = Red | Orange | Yellow | Green | Blue |Indigo | Violet deriving Show data Exp = Int Int | Plus Exp Exp | Minus Exp Exp deriving (Eq,Show)
Type propagation in the Class System • Class information propagates from the use of functions with qualified types into the functions they are used to define: • Example: member x [] = False member x (z:zs) = if x==z then True else member x zs • Note that the type of (==) (==) :: Eq a => a -> a -> Bool has propagated into the type of member member :: Eq a => a -> [a] -> Bool
Implicit invariants of Type Classes • When we define a type class (especially those with multiple methods) we often want some things to be true about the way the methods interact. • In Haskell we can’t make these invariants explicit • E.g. class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x==y) Invariants a == b => b == a a === a a == b && b == c => a == c
Another Invariant example class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a • Invariants • Exactly one of the following is true for all x,y::a 1) x < y 2) x == y 3) x > y • Transitivity x <= y && y <= z => x <= z
The fix point operator fix :: (a -> a) -> a fix f = f ( fix f ) • By unrolling the definition of fix we see fix f = f ( f ( fix f ) ) We get repeated application of f. In fact we get infinite repeated application of f • Instantiate the type of fix at (b -> c) . We get: fix :: ((b->c) -> (b->c)) -> (b -> c)
Use the eta rule fix :: ((b->c) -> (b->c)) -> (b -> c) fix f :: b -> c -- for appropriate f We can eta-expand the definition of fix fix f n = f (fix f) n
Example use f fact n = if n=0 then 1 else fact(n-1) * n note f is not recursive factorial n = fix f n fix f n = f (fix f) n = if n=0 then 1 else (fix f)(n-1) * n = if n=0 then 1 else f (fix f) (n-1) * n = if n=0 then 1 else if (n-1)=0 then 1 else (fix f) (n-2) * n