05 Types and Type Classes
Dr. Paulo Oliva / Prof. Edmund Robinson
Types and Type Classes
ECS713
Functional Programming
Week 5 Quiz
• https://qmplus.qmul.ac.uk/mod/quiz/
view.php?id=1775351
• During lab this week: Friday 29th October:
14.05 – 15.55
• See web page for coverage.
• Open book.
https://qmplus.qmul.ac.uk/mod/quiz/view.php?id=1775351
https://qmplus.qmul.ac.uk/mod/quiz/view.php?id=1775351
Week 5: Contents
• Review: type and data
• Records
• Type classes
• Built-in classes: Eq, Ord, Num, Show, Read
• Creating new type classes
Types: Record notation
– understand how to use Haskell’s record notation for user defined typesWeek 5/Task 1
Types: Polymorphism
– define polymorphic type definitions, and write type definitions that use polymorphismWeek 5/Task 1
Type Classes: Built-in type classes
– familiar with Haskell’s “built-in” types classes: Eq, Ord, Show, Read and EnumWeek 5/Task 1
Type Classes: User defined type classes
– know how to define a new type classWeek 5/Task 1
– use sub-classing to define a new type class that is a sub-class of an existing type classWeek 5/Task 1
Type Classes: Making a type an instance of a type class
– know how to use the “deriving” keyword to make a new type an instance of the basic type classesWeek 5/Task 1
– know how to use the “instance” keyword to make a new type an instance of a type classWeek 5/Task 1
• be able to use Haskell “record” notation
• know what “polymorphism” means, and identify uses
of it in Haskell
• know what a “type class” is, and can give examples of
built-in Haskell type classes
• be able to define a new type class
• be able to make an existing type an instance of a
given type class
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#record-syntax
http://learnyouahaskell.com/types-and-typeclasses#type-variables
http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102
http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102
Review: type
and data
Type Synonyms
type String = [Char]
type Position = (Int, Int)
type Distance = Int
Use type to give a new name to an existing type
toUpperString :: [Char] -> [Char]
translate :: (Int,Int) -> Int -> (Int,Int)
Makes typing more readable:
toUpperString :: String -> String
translate :: (Int,Int) -> Int -> (Int,Int)
toUpperString :: String -> String
translate :: Position -> Distance -> Position
The “data” Keyword
type Position = (Int,Int)
data Direction = North | South | East | West
move :: Position -> Direction -> Position
move (x,y) West = (x-1,y)
move (x,y) East = (x+1,y)
move (x,y) South = (x,y-1)
move (x,y) North = (x,y+1)
Use data to create a new type
we can immediately do
pattern matching on the
new type constructors
Haskell’s record types
Records
data Person = Person { fName :: String
, lName :: String
, age :: Int
}
mike = Person{fName=”Michael”,lName=”Diamond”,age=43}
adam = Person{fName=”Adam”,lName=”Horovitz”,age=41}
Prelude> :type fName
fName :: Person -> String
Prelude> fName mike
“Michael”
Prelude> age mike
43
Basic problem: position vs name
data PersonRecord = PersonR { fName :: FName
, lName :: LName
, age :: Int
} (deriving Show)
type FName = String
type LName = String
type Age = Int
data PersonCurried = PersonC FName LName Age
(deriving Show)
data PersonUncurried = PersonU (FName,LName,Age)
(deriving Show)
Prelude> :type (PersonC, PersonU, PersonR)
(PersonC, PersonU, PersonR)
:: (FName -> LName -> Age -> PersonCurried,
(FName, LName, Age) -> PersonUncurried,
FName -> LName -> Int -> PersonRecord)
Creating values of record type
data PersonRecord = PersonR { fName :: FName
, lName :: LName
, age :: Int
} (deriving Show)
mike = Person{fName=”Michael”,lName=”Diamond”,age=43}
adam = Person “Adam” “Horovitz” 41
mikeOlder = mike{age=44}
*Main> mike
PersonR {fName = “Michael”, lName = “Diamond”, age = 43}
*Main> adam
PersonR {fName = “Adam”, lName = “Horovitz”, age = 41}
*Main> mikeOlder
PersonR {fName = “Michael”, lName = “Diamond”, age = 44}
Accessing Fields
data PersonRecord = PersonR { fName :: FName
, lName :: LName
, age :: Int
} (deriving Show)
mike = Person{fName=”Michael”,lName=”Diamond”,age=43}
adam = Person “Adam” “Horovitz” 41
mikeOlder = mike{age=44}
*Main> age(mike)
43
*Main> :t age
age :: PersonRecord -> Int
Defining Functions
data PersonRecord = PersonR { fName :: FName
, lName :: LName
, age :: Int
} (deriving Show)
isOld (PersonR {age=a}) = a > 50
isOld’ (Person _ _ a) = a > 50
isOld’’ p = age(p) > 50
*Main> isOld(mike)
False
*Main> :t isOld
isOld :: PersonRecord -> Bool
More records
data PersonRecord = PersonR { fName :: FName
, lName :: LName
, age :: Int
} (deriving Show)
• You can’t have another record using the same field
name
• If you did, you would have two access functions
with the same name and different types.
Type inference and
polymorphism
inference v checking
• type inference – the language works out
what types things have
• type checking – the language checks that the
types you have given are consistent
Basic rules of Haskell type
inference
1. An identifier should be assigned the same type throughout its scope.
2. In an “if-then-else” expression, the condition must have type Bool
and the “then” and “else” portions must have the same type. The type
of the expression is the type of the “then” and “else” portions.
3. A user-defined function has type a → b, where a is the type of the
function’s parameter and b is the type of its result.
4. In a function application of the form f x, there must be types a and b
such that f has type a → b and x has type a, and the application itself
has type b.
Example
func baseAmt str = replicate
rptAmt newStr
where
rptAmt = if baseAmt > 5
then baseAmt
else baseAmt + 2
newStr = “Hello “ ++ str
newStr = “Hello “ ++ str
(++) :: [a] -> [a] -> [a]
“Hello “ :: [Char]
So a=Char and:
str :: [Char]
newstr :: [Char]
Example
func baseAmt str = replicate
rptAmt newStr
where
rptAmt = if baseAmt > 5
then baseAmt
else baseAmt + 2
newStr = “Hello “ ++ str
5 has Num type, as does 2
(+)::Num a => a -> a -> a
So baseAmt has Num type, as
does baseAmt + 2.
(>)::Ord a => a -> a -> Bool
So baseAmt also has ord type.
And rptAmt has the same type
as baseAmt.
Example
func baseAmt str = replicate
rptAmt newStr
where
rptAmt = if baseAmt > 5
then baseAmt
else baseAmt + 2
newStr = “Hello “ ++ str
replicate :: Int -> a -> [a]
So baseAmt :: Int (which is
both a Num type and an Ord
type).
We know newStr :: [Char]
So a = [Char]
Hence result is [[Char]]
func :: Int -> [Char] -> [[Char]]
Type deduction
• When we do this we sometimes get a type
like this.
• We sometimes get a contradiction (the
code is untypable).
• We sometimes get some type variables left
(the code is polymorphic).
Example
foldr f b [] = b
foldr f b (a:as) =
f a (foldr b as)
foldr :: f -> b -> c -> b
Example
foldr f b [] = b
foldr f b (a:as) =
f a (foldr f b as)
foldr :: f -> b -> [c] -> b
(a:as)::[c], and
(:) :: c -> [c] -> [c]
So a::c
and as :: [c]
Example
foldr f b [] = b
foldr f b (a:as) =
f a (foldr b as)
foldr :: f -> b -> [c] -> b
So
foldr :: (c -> b -> b) -> b -> [c] -> b
(a:as)::[c], and
(:) :: c -> [c] -> [c]
So a::c
and as :: [c]
f :: c -> b -> b
Type classes
Type classes
defining your own type class
Haskell Type Classes
• You can think of a type class as a collection of types
• A type class defines an “interface”, a list of
functions that must be implemented in order to
make a type an “instance” of the type class
class Shape a where
perimeter :: a -> Double
area :: a -> Double
Here we are defining
the type class Shape
A type a can be made an instance of
Shape if we can implement the
functions perimeter :: a -> Double
and area :: a -> Double
Type classes
making a type an instance of a class
class Shape a where
perimeter :: a -> Double
area :: a -> Double
type Side = Double
type Radius = Double
data Square = Square Side
data Circle = Circle Radius
instance Shape Square where
perimeter (Square x) = 4 * x
area (Square x) = x^2
instance Shape Circle where
perimeter (Circle r) = 2 * r * pi
area (Circle r) = pi * r^2
Two new types:
Square and Circle
Square is now a
member of the type
class Shape
Circle can also be
made an member of
the type class Shape
class Shape a where
perimeter :: a -> Double
area :: a -> Double
type Side = Double
type Radius = Double
data Square = Square Side
data Circle = Circle Radius
instance Shape Square where
perimeter (Square x) = 4 * x
area (Square x) = x^2
instance Shape Circle where
perimeter (Circle r) = 2 * r * pi
area (Circle r) = pi * r^2
Type class
member
type
must be a
data type
not a type synonym
Type classes
what they are
$ ghci
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading packages …
Prelude> :type div
div :: Integral a => a -> a -> a
Prelude> :type qsort
qsort :: Ord a => [a] -> [a]
Prelude> :type elem
elem :: Eq a => a -> [a] -> Bool
Prelude> :type show
show :: Show a => a -> String
Type classes enable function name overloading
You can use div on any
type in the Integral class
You can use qsort on any
type in the Ord class
You can use elem on any
type in the Eq class
You can use show on any
type in the Show class
http://www.haskell.org/ghc/
Type Class
A collection of types that support certain
overloaded operations called methods
An interface that defines some behaviour
Corresponds to interfaces in OO programming
$ ghci
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading packages …
Prelude> :type div
div :: Integral a => a -> a -> a
Prelude> :type qsort
qsort :: Ord a => [a] -> [a]
Prelude> :type elem
elem :: Eq a => a -> [a] -> Bool
Prelude> :type show
show :: Show a => a -> String
any type “a” that
supports division
any type “a” that
has a notion of order
any type “a” that
supports equality test
any type “a” that can be
converted into string
Type Class
http://www.haskell.org/ghc/
$ ghci
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading packages …
Prelude> let f x y = if x==y then False else True
Prelude> :type f
f :: Eq a => a -> a -> Bool
Prelude> let g x y = if x
g :: (Ord a, Num b) => a -> a -> b
any type “a” that
supports equality test
any type “a” that
has a notion of order
any numeric type “b”
Type Class
http://www.haskell.org/ghc/
Type classes
standard built-in classes
Haskell Type Classes
• Several type classes are pre-defined in Haskell:
✦ Eq: For types that support equality test ==
✦ Ord: For Eq types that can also be ordered
✦ Show: For types that can be mapped to String
✦ Read: For types that can be mapped from String
✦ Num: For numeric types, i.e. supporting +, *,…
✦ Fractional: For numeric types that also support /
Num
Integral Fractional
Numerical Types/Classes
Double
FloatInt
Integer
fixed precision
integers
arbitrary precision
integers
single precision
double precision
Numerical Types
— addition has type
(+) :: Num a => a -> a -> a
Fractional Types
— division has type
(/) :: Fractional a => a -> a -> a
The Eq Type Class
— all types which support equality test
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x==y)
instance Eq Bool where
False == False = True
True == True = True
_ == _ = False
essentially an interface
describing signature
of available methods
any concrete instance
has to implement the
methods
Sub-classing
— ordered types are equality types with an extra
— order relation
class Eq a => Ord a where
(<),(≤),(>),(≥) :: a -> a -> Bool
min, max :: a -> a -> a
min x y | x ≤ y = x
| otherwise = y
max x y | x ≤ y = y
| otherwise = x
Ordered Types
— insert new element on sorted list
insert :: Ord a => a -> [a] -> [a]
insert x [] = [x]
insert x (y:ys) | x ≤ y = x : y : ys
| otherwise = y : insert x ys
— quick sort
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger
where
smaller = [a | a <- xs, a ≤ x] larger = [b | b <- xs, b > x]
Built-in Type Classes
(Show and Read)
Show / Read
— types that support conversion to String
class Show a where
show :: a -> String
— types that support conversion from String
class Read a where
read :: String -> a
Show / Read
$ ghci
GHCi, version 6.12.3: http://www.haskell.org/ghc/
Loading packages …
Prelude> show True
“True”
Prelude> show [1,2,3]
“[1,2,3]”
Prelude> 1 + read “2”
3
Prelude> read “[1,2,3]”
Prelude> read “[1,2,3]” :: [Int]
[1,2,3]
http://www.haskell.org/ghc/
Derived Instances
— For instance, Bool is defined as
data Bool = False | True
deriving (Eq, Ord, Show, Read)
— the following is also ok because Float is an
— Eq type
data Shape = Circle Float | Rect Float Float
deriving Eq
new types can be made into instances
of the built-in type classes using
the deriving keyword
Week 5 Quiz
• https://qmplus.qmul.ac.uk/mod/quiz/
view.php?id=1775351
• During lab this week: Friday 29th October:
14.05 – 15.55
• See web page for coverage.
• Open book.
https://qmplus.qmul.ac.uk/mod/quiz/view.php?id=1775351
https://qmplus.qmul.ac.uk/mod/quiz/view.php?id=1775351