Type Classes

Slides

Quizzes

cse116-plus-type-ind -> E

cse116-ord-ind -> C

cse116-read-ind -> A

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?

  1. the types of get and add: get :: (Ord k) => k -> Env k v -> v need to add Ord

  2. the 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