Last Week
The interpreter:
- How do we evaluate a program given its abstract syntax tree (AST)?
Next two Weeks
Modern language features for structuring programs
- Type classes
- Monads
that will help us add cool features to the Nano interpreter!
Type Classes: Outline
- Why type classes?
- Standard type classes
- Creating new instances
- Typeclasses and Polymorphism
- Creating new type classes
Overloading Operators: Arithmetic
The + operator works for a bunch of different types.
For Integer:
λ> 2 + 3
5for Double precision floats:
λ> 2.9 + 3.5
6.4
Overloading Comparisons
Similarly we can compare different types of values
λ> 2 == 3
False
λ> [2.9, 3.5] == [2.9, 3.5]
True
λ> ("cat", 10) < ("cat", 2)
False
λ> ("cat", 10) < ("cat", 20)
True
Operator Overloading
Seems unremarkable?
Languages have supported “operator overloading” since the dawn of time
Haskell has no caste system
No distinction between operators and functions
- All are first class citizens!
You can implement functions like (+) and (==) yourself from scratch!
- But then, what type do we give them?
QUIZ
Which of the following type annotations would work for (+) ?
(A) (+) :: Int -> Int -> Int
(B) (+) :: Double -> Double -> Double
(C) (+) :: a -> a -> a
(D) Any of the above
(E) None of the above
Int -> Int -> Int is bad because?
- Then we cannot add
Doubles!
Double -> Double -> Double is bad because?
- Then we cannot add
Ints!
a -> a -> a is bad because?
I don’t know how to implement this
For some
as it doesn’t make sense: how do I add twoBools? Or twoChars?
Ad-hoc Polymorphism
We have seen parametric polymorphism:
-- Append two lists:
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x:(xs ++ ys)(++) works for all list types
Doesn’t care what the list elements are
The same implementation works for
a = Int,a = Bool, etc.
Now we need ad-hoc polymorphism:
(+) :: a -> a -> a -- Almost, but not really
(+) x y = ???(+) should work for many (but not all) types
- Different implementation for
a = Int,a = Double, etc.
Ad-hoc means “created or done for a particular purpose as necessary.”
Type Classes for Ad Hoc Polymorphism
Haskell solves this problem with a mechanism called type classes
- Introduced by Wadler and Blott

This is a very cool and well-written paper! Read it!
Constrained Types
Let’s ask GHCi:
λ> :type (+)
(+) :: (Num a) => a -> a -> aWe call this a constrained (or qualified) type
Read it as:
(+)takes in twoavalues and returns anavaluefor any type
athatis an instance of the
Numtype classor, in Java terms: implements the
Numinterface
The “(Num a) =>” part is called the constraint
Some types are Nums:
- For example,
Int,Integer,Double - Values of those types can be passed to
(+):
λ> 2 + 3
5
Other types are not Nums:
- For example,
Bool,Char,String, function types, … - Values of those types cannot be passed to
(+):
λ> True + False
<interactive>:15:6:
No instance for (Num Bool) arising from a use of ‘+’
In the expression: True + False
In an equation for ‘it’: it = True + False
Aha! Now those no instance for error messages should make sense!
- Haskell is complaining that
TrueandFalseare of typeBool - and that
Boolis not an instance ofNum
QUIZ
What would be a reasonable type for the equality operator?
(A) (==) :: a -> a -> a
(B) (==) :: a -> a -> Bool
(C) (==) :: (Eq a) => a -> a -> a
(D) (==) :: (Eq a) => a -> a -> Bool
(E) None of the above
Type Classes: Outline
- Why type classes? [done]
- Standard type classes
- Creating new instances
- Typeclasses and Polymorphism
- Creating new type classes
What is a type class?
A type class is a collection of methods (functions, operations) that must exist for every instance
What are some useful type classes in the Haskell standard library?
The Eq Type Class
The simplest typeclass is Eq:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> BoolA type T is an instance of Eq if there are two functions
(==) :: T -> T -> Boolthat determines if twoTvalues are equal(/=) :: T -> T -> Boolthat determines if twoTvalues are disequal
Lifehack: You can ask GHCi about a type class and it will tell you
- its methods
- all the instances it knows
λ> :info Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
...
instance Eq Int
instance Eq Double
...
The Ord Typeclass
The type class Ord is for totally ordered values:
class Eq a => Ord a where
(<) :: a -> a -> Bool
(<=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(>=) :: a -> a -> BoolFor example:
λ> 2 < 3
True
λ> "cat" < "dog"
True
Note Eq a => in the class definition!
A type T is an instance of Ord if
Tis also an instance ofEq, and- It defines functions for comparing values for inequality
The Num Type Class
The type class Num requires that instances define a bunch of arithmetic operations
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
The Show Type Class
The type class Show requires that instances be convertible to String
class Show a where
show :: a -> StringIndeed, we can test this on different (built-in) types
λ> show 2
"2"
λ> show 3.14
"3.14"
λ> show (1, "two", ([],[],[]))
"(1,\"two\",([],[],[]))"
The Read Typeclass
-- Not the actual definition, but almost:
class Read a where
read :: String -> aRead is the opposite of Show
It requires that every instance
Tcan parse a string and turn it intoTJust like with
Show, most standard type are instances ofRead:Int,Integer,Double,Char,Bool, etc
QUIZ
What does the expression read "2" evaluate to?
(A) Type error
(B) "2"
(C) 2
(D) 2.0
(E) Run-time error
Haskell is foxed!
- Doesn’t know what type to convert the string to!
- Doesn’t know which of the
readfunctions to run!
Did we want an Int or a Double or maybe something else altogether?
Thus, here an explicit type annotation is needed to tell Haskell what to convert the string to:
λ> (read "2") :: Int
2
λ> (read "2") :: Float
2.0
λ> (read "2") :: String
???Note the different results due to the different types.
Standard Typeclass Hierarchy
Haskell comes equipped with a rich set of built-in classes.
In the above picture, there is an edge from Eq to Ord
because for something to be an Ord it must also be an Eq.
Type Classes: Outline
- Why type classes? [done]
- Standard type classes [done]
- Creating new instances
- Typeclasses and Polymorphism
- Creating new type classes
Showing Your Colors
Let’s create a new datatype:
data Color = Red | Greenand play with it in GHCi:
λ> let col = Red
λ> :type col
x :: ColorSo far, so good… but we cannot view them!
λ> col
<interactive>:1:0:
No instance for (Show Color)
arising from a use of `print' at <interactive>:1:0
Possible fix: add an instance declaration for (Show Color)
In a stmt of a 'do' expression: print itWhy is this happening?
When we type an expression into GHCi it:
- evaluates it to a value, then
- calls
showon that value to convert it to a string
But our new type is not an instance of Show!
We also cannot compare colors!
λ> col == Green
<interactive>:1:0:
No instance for (Eq Color)
arising from a use of `==' at <interactive>:1:0-5
Possible fix: add an instance declaration for (Eq Color)
In the expression: col == Green
In the definition of `it': it = col == Green
How do we add an instance declaration for Show Color and Eq Color?
Creating Instances
To tell Haskell how to show or compare values of type Color
- create instances of
EqandShowfor that type:
instance Show Color where
show Red = "Red"
show Green = "Green"
instance Eq Color where
???
EXERCISE: Creating Instances
Create an instance of Eq for Color:
data Color = Red | Green
instance Eq Color where
???
-- Reminder:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
QUIZ
Which of the following Eq instances for Color are valid?
-- (A)
instance Eq Color where
(==) Red Red = True
(==) Green Green = True
(==) _ _ = False
-- (B)
instance Eq Color where
(/=) Red Red = False
(/=) Green Green = False
(/=) _ _ = True
-- (C) Neither of the above
-- (D) Either of the above
Default Method Implementations
The Eq class is actually defined like this:
class Eq a where
(==) :: a -> a -> Bool
(==) x y = not (x /= y) -- Default implementation!
(/=) :: a -> a -> Bool
(/=) x y = not (x == y) -- Default implementation!
The class provides default implementations for its methods
An instance can define any of the two methods and get the other one for free
Use
:infoto find out which methods you have to define:
λ> :info Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
{-# MINIMAL (==) | (/=) #-} -- HERE HERE!!!
QUIZ
If you define:
instance Eq Color where
-- Nothing here!what will the following evaluate to?
λ> Red == Green(A) Type error
(B) Runs forever / stack overflow
(C) Other runtime error
(D) False
(E) True
Automatic Derivation
This is silly: we should be able to compare and view Colors “automatically”!
Haskell lets us automatically derive functions for some classes in the standard library.
To do so, we simply dress up the data type definition with
data Color = Red | Green
deriving (Eq, Show) -- please generate instances automatically!Now we have
λ> let col = Red
λ> col
Red
λ> col == Red
True
Type Classes: Outline
- Why type classes? [done]
- Standard type classes [done]
- Creating new instances [done]
- Typeclasses and Polymorphism
- Creating new type classes
Let’s build a small library for dictionaries mapping keys k to values v
data Dict k v
= Empty -- empty dictionary
| Bind k v (Dict k v) -- bind key `k` to the value `v`
deriving (Show)
The API
We want to be able to do the following with Dict:
>>> let dict0 = add "cat" 10.0 (add "dog" 20.0 empty)
>>> get "cat" dict0
10
>>> get "dog" dict0
20
>>> get "horse" dict0
error: key not found
Okay, lets implement!
-- | 'add key val dict' returns a new dictionary
-- | that additionally maps `key` to `val`
add :: k -> v -> Dict k v -> Dict k v
add key val dict = ???
-- | 'get key dict' returns the value of `key`
get :: k -> Dict k v -> v
get key dict = ???
But we get a type error!
Constraint Propagation
Lets delete the types of add and get and see what Haskell says their types are!
λ> :type get
get :: (Eq k) => k -> v -> Dict k v -> Dict k vHaskell tells us that we can use any k type as a key
as long as this type is an instance of the Eq typeclass.
How, did GHC figure this out?
- If you look at the code for
getyou’ll see that we check if two keys are equal!
EXERCISE: A Faster Dictionary
Write an optimized version of
addthat ensures the keys are in increasing ordergetthat gives up the moment we see a key that’s larger than the one we’re looking for
(How) do you need to change the types of get and add?
Type Classes: Outline
- Why type classes? [done]
- Standard type classes [done]
- Creating new instances [done]
- Typeclasses and Polymorphism [done]
- Creating new type classes
Creating Typeclasses
Typeclasses are useful for many different things
- Improve readability
- Promote code reuse
We will see some very interesting use cases over the next few lectures.
Lets conclude today’s class with a quick example that provides a taste.
Dictionary with Default Values
Recall that our get function throws an error when the key is not found:
get key Empty = error "key not found"
get key (Bind oldK v rest)
| key == oldK = v
| key < oldK = error "key not found"
| otherwise = get key rest
Let’s modify our implementation of fast dictionaries
so that get returns a default value instead:
λ> get 2 (add 5 "five" (add 10 "ten" (add 3 "three" Empty)))
""
λ> get "horse" (add "cat" 10.0 (add "dog" 20.1 Empty))
0.0
How can we achieve this?
Option 1: add an argument to get
get def key Empty = def
get def key (Bind oldK v rest)
| key < oldK = def
| ...But then the client has to do:
λ> let dict = add 5 "five" (add 10 "ten" (add 3 "three" Empty))
λ> get "" 2 dict
""
-- Need to pass in an extra argument every time! annoying!
λ> get "" 5 dict
"five"
Option 2: store the default value in the dictionary
data Dict k v
= Empty v -- store the default value here
| Bind k v (Dict k v) v -- and maybe also hereLimitations?
Type Classes to the Rescue!
Would it be too much to ask for a magical defValue…
get def key Empty = defValue
get def key (Bind oldK v rest)
| key < oldK = defValue
| ...… which has different values depending on the type of v?
λ> get 2 (add 5 "five" (add 10 "ten" (add 3 "three" Empty)))
"" -- default value for strings
λ> get "horse" (add "cat" 10.0 (add "dog" 20.1 Empty))
0.0 -- default value for floats
That’s what type classes are for!
-- (<) is a magical function
-- with different implementations depending on the argument type:
λ> 1 < 2
True
λ> "cat" < "dog"
True
Type Class for Default Values
class Default a where
defValue :: aNow we need to tell get that v has a default value:
get :: (Ord k, Default v) => k -> Dict k v -> v
get key Empty = defValue -- uses the method of the Default class!
get key (Bind oldK v rest)
| key < oldK = defValue
| key == oldK = v
| otherwise = get key restFinally, we need to define instances of Default for different value types:
instance Default Int where
defValue = 0
instance Default [a] where
defValue = []
That’s all folks!