Type Classes¶
Intro¶
Let’s think about overloading operators - 1 + 1
and 1.0 + 1.1
work slightly differently
This is ad-hoc overloading - to compare/add values of multiple types
Note: Haskell has no caste system, so functions are first-class citizens; what class are operators then?
Qualified Types¶
:type (+)
(+) :: (Num a) => a -> a -> a
+
takes in any class that is an instance of or implements Num
- Num is a predicate/constraint
A typeclass is a collection of operations that must exist for the underlying type.
Eq¶
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
A type a is an instance of Eq
if these operations exist on it.
Creating Instances¶
data Unshowable = A | B | C
instance Eq Unshowable where
(==) A A = True
(==) B B = True
(==) C C = True
(==) _ _ = False
(/=) x y = not (x == y)
Automatic Derivation¶
data Showable = A' | B' | C'
deriving (Eq, Show)
Haskell can automatically generate instances!
Standard Typeclass Hierarchy¶
class (Eq a, Show a) => Num a where -- all Nums must derive from Eq and Show
(+) :: a -> a -> a
...
Using Typeclasses¶
Let’s build a small lib for environments mapping keys to values:
data Env k v
= Def v -- default
| Bind k v (Env k v) -- bind k to v, recursive structure
deriving (Show)
-- API:
-- >>> let env0 = add "cat" 10.0 (add "dog" 20.0 (Def 0))
-- >>> get "cat" env0
-- 10
-- >>> get "dog" env0
-- 20
-- >>> get "horse" env0
-- 0
-- implementation:
add :: k -> v -> Env k v -> Env k v
add key val env = Bind key val env
get :: (Eq k) => k -> Env k v -> v -- note that k has to derive Eq!
get key (Def v) = v
get key (Bind ek ev env) | k == ek = ev
| otherwise = get key env
What about an optimized version that stores keys in increasing order, to optimize add and get?
the types of get and add:
get :: (Ord k) => k -> Env k v -> v
need to add Ordthe type of Env: move the default so that we don’t have to recurse to the end
Explicit Signatures¶
In some cases using typeclasses, explicit signatures are required:
e.g. read :: (Read a) => String -> a
, the opposite of Show
We have to do: (read "2") :: Int
or (read "2") :: Float