Gentle Introduction to Haskell 98, Online Supplement
Part 12
Covers Section 5
> module Part12() where
> import Prelude hiding (elem)
Section: 5 Type Classes
Names in the basic class structure of Haskell cannot be hidden (they are
in PreludeCore) so we have to modify the names used in the tutorial.
Here is a new Eq class:
> class Eq' a where
> eq :: a -> a -> Bool
Now we can define elem using eq from above:
> elem :: (Eq' a) => a -> [a] -> Bool
> x `elem` [] = False
> x `elem` (y:ys) = x `eq` y || x `elem` ys
Before this is of any use, we need to admit some types to Eq'
> instance Eq' Int where
> x `eq` y = abs (x-y) < 3 -- Let's make this `nearly equal' just for fun
> instance Eq' Float where
> x `eq` y = abs (x-y) < 0.1
> list1 :: [Int]
> list1 = [1,5,9,23]
> list2 :: [Float]
> list2 = [0.2,5.6,33,12.34]
> e1 = 2 `elem` list1
> e2 = 100 `elem` list1
> e3 = 0.22 `elem` list2
Watch out! Integers in Haskell are overloaded - without a type signature
to designate an integer as an Int, expressions like 3 `eq` 3 will be
ambiguous.
Now to add the tree type:
> data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show
> instance (Eq' a) => Eq' (Tree a) where
> (Leaf a) `eq` (Leaf b) = a `eq` b
> (Branch l1 r1) `eq` (Branch l2 r2) = (l1 `eq` l2) && (r1 `eq` r2)
> _ `eq` _ = False
> tree1,tree2 :: Tree Int
> tree1 = Branch (Leaf 1) (Leaf 2)
> tree2 = Branch (Leaf 2) (Leaf 1)
> e4 = tree1 `eq` tree2
Now make a new class with Eq' as a super class:
> class (Eq' a) => Ord' a where
> lt,le :: a -> a -> Bool -- lt and le are operators in Ord'
> x `le` y = x `eq` y || x `lt` y -- This is a default for le
The typing of lt & le is
le,lt :: (Ord' a) => a -> a -> Bool
This is identical to
le,lt :: (Eq' a,Ord' a) => a -> a -> Bool
Make Int an instance of Ord':
> instance Ord' Int where
> x `lt` y = x < y+1
> i :: Int -- Avoid ambiguity
> i = 3
> e5 :: Bool
> e5 = i `lt` i
Some constraints on instance declarations:
A program can never have more than one instance declaration for
a given combination of data type and class.
If a type is declared to be a member of a class, it must also be
declared in all superclasses of that class.
An instance declaration does not need to supply a method for every
operator in the class. When a method is not supplied in an
instance declaration and no default is present in the class
declaration, a runtime error occurs if the method is invoked.
You must supply the correct context for an instance declaration --
this context is not inferred automatically.
This definition of Functor is in the Prelude:
class Functor f where
fmap :: (a -> b) -> f a -> f b -- a generalized map function
This makes Tree an instance of Functor.
> instance Functor Tree where
> fmap f (Leaf x) = Leaf (f x)
> fmap f (Branch t1 t2) = Branch (fmap f t1) (fmap f t2)
> e6 = fmap (+1) tree1
We can also make 2-tuples an instance of Functor:
> instance Functor ((,) a) where
> fmap f (x,y) = (x,f y)
> e7 = fmap (+1) (1,2)
Note that these are a kind errors:
> -- instance Functor (,)
> -- instance Functor Bool
The error message from Hugs isn't very useful but if you use :set +k then
you will see a better message.
Continued in part13.lhs