Lecture 1 — Functional Programming
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
1
CO2008 Functional Programming (In a nutshell)
Write a program to add up the first n square numbers:
sumofsqs n = 12 + 22 + … + (n-1)2 + n2
Clear Haskell solution:
sumSquares :: Int -> Int
sumSquares 1 = 1
sumSquares n = n*n + sumSquares (n-1)
Less clear Java solution:
public int sumSquares(int n){
private int s,i;
s=0; i=0;
while (i
• Functions: Our program is a function
hssquares 1 = 1
hssquares n = n*n + hssquares (n-1)
• Evaluation: Run the program by expanding definitions
hssquares 3 ) 3*3+ hssquares 2
) 9 + (2*2 + hssquares 1)
) 9 + (4 + 1) ) 14
• Comment: No mention of memory in the code.
Fer-Jan de Vries Leicester, February 16, 2017
7
Key Ideas in Functional Programming I — Types
• Motivation: Recall from CO1003/4 that types model data.
• Integers: Int is the Haskell type {. . . ,�2,�1,0,1,2, . . .}
• String: String is the Haskell type of lists of characters.
• Complex Datatypes: Can be made from the basic types, eg
lists of integers.
• Built in Operations (”Functions on types”):
– Arithmetic Operations: + * – div mod abs
– Ordering Operations: > >= == /= <= <
Fer-Jan de Vries Leicester, February 16, 2017
8
Reminder
– Arithmetic Operations: + * - div mod abs
– div is the (Euclidean) division on integers. It is the process
of division of two integers, which produces a quotient and a
remainder. div outputs the quotient. 17 "div" 3 = 5.
– mod is the modulo operation on integers. It finds the re-
mainder after division of one number by another (sometimes
called modulus). 5 "mod" 3 = 2, 8 "mod" 2 = 0.
– abs is the absolute value function on integers. It assigns x
to a positive integer x and �x to a negative integer x.
abs (-1) = abs (1) = 1
Fer-Jan de Vries Leicester, February 16, 2017
9
Key Ideas in Functional Programming II — Functions
• Intuition: Recall a function (or if you prefer method) f : a ! b
between sets associates to every input-value a unique output-
value
x 2 a �! Function f ?�! y 2 b
• Example: The square and cube functions are written
square :: Int -> Int cube :: Int -> Int
square x = x * x cube x = x * square x
• In General: In Haskell, functions are defined as follows
h function-namei :: h input typei->h output typei
h function-namei h variablei = h expressioni
Fer-Jan de Vries Leicester, February 16, 2017
10
Functions with Multiple Arguments
• Intuition: A function f with n inputs is written f::a1->…-> an-> a
x1 2 a1 �!
x2 2 a2 �!
… …
x
n
2 a
n
�!
Function f ?�! y 2 a
• Example: The ”distance” between two integers
diff :: Int -> Int -> Int
diff x y = abs (x – y)
• In General:
h function-namei :: h type 1i-> . . . ->h type ni->h output-typei
h function-namei h variable 1i . . . h variable ni = h expressioni
Fer-Jan de Vries Leicester, February 16, 2017
11
Key Idea III — Expressions
• Motivation: Get the result/output of a function by applying it
to an argument/input
– Write the function name followed by the input
• In General: Application is governed by the typing rule
– If f is a function of type a->b, and e is an expression of type
a,
– then f e is the result of applying f to e and has type b
• Key Idea: Expressions are fragments of code built by applying
functions to arguments.
square 4 square (3 + 1) square 3 + 1
cube (square 2) diff 6 7 square 2.2(?)
Fer-Jan de Vries Leicester, February 16, 2017
12
Key Ideas in Functional Programming IV — Evaluating Expressions
• More Expressions: Use quotes to turn functions into infix operations and
brackets to turn infix operations into functions
5 * 4 (*) 5 4 mod 13 4 13 ‘mod‘ 4
5-(3*4) (5-3)*4 7 >= (3*3) 5 * (-1)
• Precedence: Usual rules of precedence and bracketing apply
• Example of Evaluation:
cube(square3) ) (square 3) * square (square 3)
) (3*3) * ((square 3) * (square 3))
) 9 * ((3*3) * (3*3))
) (9 * (9*9))
) 729
• The final outcome of an evalution is called a value
Fer-Jan de Vries Leicester, February 16, 2017
13
Summary — Comparing Functional and Imperative Programs
• Di↵erence 1: Level of Abstraction
– Imperative Programs include low level memory details
– Functional Programs describe only high-level algorithms
• Di↵erence 2: How execution works
– Imperative Programming based upon memory transformation
– Functional Programming based upon expression evaluation
• Di↵erence 3: Type systems
– Type systems play a key role in functional programming
Fer-Jan de Vries Leicester, February 16, 2017
14
Today You Should Have Learned …
• Types: A type is a collection of data values
• Functions: Transform inputs to outputs
– We build complex expressions by defining functions and ap-
plying them to other expressions
– The simplest (evaluated) expressions are (data) values
• Evaluation: Calculates the result of applying a function to an
input
– Expressions can be evaluated to values by hand or by GHCi
(the interpreter of the Glasgow Haskell Compiler)
• Now: Go and look at the first practical!
Fer-Jan de Vries Leicester, February 16, 2017
15
Lecture 2 — More Types and Functions
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
16
Overview of Lecture 2
• New Types: Today we shall learn about the following types
– The type of booleans: Bool
– The type of characters: Char
– The type of strings: String
– The type of fractions: Float
• New Functions and Expressions: And also about the follow-
ing functions
– Conditional expressions and guarded functions
– Error handling and local declarations
Fer-Jan de Vries Leicester, February 16, 2017
17
Booleans and Logical Operators
• Values of Bool : Contains two values — True, False
• Logical Operations: Various built in functions
&& :: Bool -> Bool -> Bool
|| :: Bool -> Bool -> Bool
not :: Bool -> Bool
• Example: Define the exclusive-OR function which takes as
input two booleans and returns True just in case they are dif-
ferent
exOr :: Bool -> Bool -> Bool
Fer-Jan de Vries Leicester, February 16, 2017
18
Conditionals — If statements
• Example: Maximum of two numbers
maxi :: Int -> Int -> Int
maxi n m = if n>=m then n else m
• Example: Testing if an integer is 0
isZero :: Int -> Bool
isZero x = if (x == 0) then True else False
• Conditionals: A conditional expression has the form
if b then e1 else e2
where
– b is an expression of type Bool
– e1 and e2 are expressions with the same type
Fer-Jan de Vries Leicester, February 16, 2017
19
Guarded functions — An alternative to if-statements
• Example: doubleMax returns double the maximum of its inputs
doubleMax :: Int -> Int -> Int
doubleMax x y
| x >= y = 2*x
| x < y = 2*y
• Definition: A guarded function is of the form
hfunction-namei :: htype 1i-> . . . ->htype ni ->houtput typei
hfunction-namei hvar 1i . . . hvar ni
| hguard 1i = hexpression 1i
| . . . = . . .
| hguard mi = hexpression mi
where hguard 1i, …, hguard mi :: Bool
Fer-Jan de Vries Leicester, February 16, 2017
20
The Char type
• Elements of Char : Letters, digits and special characters
• Forming elements of Char : Single quotes form characters:
’d’ :: Char ’3’ :: Char
• Functions: Characters have codes and conversion functions
chr :: Int -> Char ord :: Char -> Int
Erratum: use import Char or :module Char to import these goodies!
• Examples: Try them out! (Also try isDigit and digitToInt)
offset :: Int
offset = ord ’A’ – ord ’a’
capitalize :: Char -> Char
capitalize ch = chr (ord ch + offset)
isLower :: Char -> Bool
isLower x = (’a’ <= x) && (x <= ’z’)
Fer-Jan de Vries Leicester, February 16, 2017
21
The String type
• Elements of String: Lists of characters
• Forming elements of String: Double quotes form strings
"Newcastle Utd" "1a"
• Special Strings: Newline and Tab characters
"Super \n Alan" "1\t2\t3" putStr( "Super \n Alan")
• Combining Strings: Strings can be combined by ++
"Super " ++ "Alan " ++ "Shearer" = "Super Alan Shearer"
• Example: duplicate gives two copies of a string
Fer-Jan de Vries Leicester, February 16, 2017
22
The type of Fractions Float
• Elements of Float : Contains decimals, eg -21.3, 23.1e-2
• Built in Functions: Arithmetic, Ordering, Trigonometric
• Conversions: Functions between Int and String
ceiling, floor, round :: Float -> Int
fromIntegral :: Int -> Float
show :: Float -> String
read :: String -> Float
• Overloading: Overloading is when values/functions belong to
several types
2 :: Int show :: Int -> String
2 :: Float show :: Float -> String
Fer-Jan de Vries Leicester, February 16, 2017
23
Error-Handling
• Motivation: Informative error messages for run-time errors
• Example: Dividing by zero will cause a run-time error
myDiv :: Float -> Float -> Float
myDiv x y = x/y
• Solution: Use an error message in a guarded definition
myDiv :: Float -> Float -> Float
myDiv x y
| y /= 0 = x/y
| otherwise = error “Attempt to divide by 0”
• Execution: If we try to divide by 0 we get
Prelude> mydiv 5 0
Program error: Attempt to divide by 0
Fer-Jan de Vries Leicester, February 16, 2017
24
Local Declarations — where
• Motivation: Functions will often depend on other functions
• Example : Summing the squares of two numbers
sq :: Int -> Int
sq x = x * x
sumSquares :: Int -> Int -> Int
sumSquares x y = sq x + sq y
• Problem: Such definitions clutter the top-level environment
• Answer: Local definitions allow auxiliary functions
sumSquares2 :: Int -> Int -> Int
sumSquares2 x y = sq x + sq y
where sq z = z * z
Fer-Jan de Vries Leicester, February 16, 2017
25
Extended Example OP
• Quadratic Equations: The solutions of ax2 + bx+ c = 0 are
�b±
q
b
2 � 4ac
2a
• Types: Our program will have type
roots :: Float -> Float -> Float -> String
• Guards: There are 4 cases to check so use a guarded definition
roots a b c
| a == 0 = ….
| b*b-4*a*c < 0 = ....
| b*b-4*a*c == 0 = ....
| otherwise = ....
Fer-Jan de Vries Leicester, February 16, 2017
26
The function roots — Stage II OP
• Code: Now we can add in the answers
roots a b c
| a == 0 = error "Not a quadratic eqn"
| b*b-4*a*c < 0 = "No roots."
| b*b-4*a*c == 0 = "One root: " ++ show (-b/2*a)
| otherwise = "Two roots: " ++
show ((-b + sqrt (b*b-4*a*c))/2*a)
++ "and" ++
show ((-b - sqrt (b*b-4*a*c))/2*a)
• Problem: This program uses several expressions repeatedly
– Being cluttered, the program is hard to read
– Similarly the program is hard to understand
– Repeated evaluation of the same expression is ine�cient
Fer-Jan de Vries Leicester, February 16, 2017
27
The final version of roots OP
• Local decs: Expressions used repeatedly are made local
roots a b c
| a == 0 = error "Not a quadratic eqn"
| disc < 0 = "No roots."
| disc == 0 = "One root: " ++ show centre
| otherwise = "Two roots: " ++
show (centre + offset) ++ "and" ++
show (centre - offset)
where
disc = b*b-4*a*c
offset = (sqrt disc) / 2*a
centre = -b/2*a
Fer-Jan de Vries Leicester, February 16, 2017
28
Today You Should Have Learned
• Types: We have learned about Haskell’s basic types. For each
type we learned
– Its basic values (elements)
– Its built in functions
• Expressions: How to write expressions involving
– Conditional expressions and Guarded functions
– Error Handling and Local Declarations
Fer-Jan de Vries Leicester, February 16, 2017
29
Lecture 3 — New Types from Old
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
30
Overview of Lecture 3
• Building New Types: Today we will learn about the following
compound types
– Pairs
– Tuples
– Type Synonyms
• Describing Types: As with basic types, for each type we want
to know
– What are the values of the type
– What expressions can we write and how to evaluate them
Fer-Jan de Vries Leicester, February 16, 2017
31
From simple data values to complex data values
• Motivation: Data for programs modelled by values of a type
• Problem: Single values in basic types too simple for real data
• Example: A point on a plane can be specified by
– A number for the x-coordinate and another for the y-coordinate
• Example: A person’s complete name could be specified by
– A string for the first name and another for the second name
• Example: The performance of a football team could be
– A string for the team and a number for the points
Fer-Jan de Vries Leicester, February 16, 2017
32
New Types from Old I — Pair Types and Expressions
• Examples: For instance
– The expression (5,3) has type (Int, Int)
– The name ("Alan","Shearer") has type (String, String)
– The performance ("Newcastle", 22) has type (String,Int)
• Question: What are the values of a pair type?
• Answer: A pair type contains pairs of values, ie
– If e1 has type a and e2 has type b
– Then (e1,e2) has type (a,b)
Fer-Jan de Vries Leicester, February 16, 2017
33
Functions using Pairs
• Types: Pair types can be used as input and/or output types
• Examples: The built in functions fst and snd are vital
fst :: (a,b) -> a
fst (x,y) = x
winUpdate :: (String,Int) -> (String,Int)
winUpdate (x,y) = (x,y+3)
movePoint :: Int -> Int -> (Int,Int) -> (Int,Int)
movePoint m n (x,y) = (x+m,y+n)
• Key Idea: If input is a pair-type, use (hvar1i, hvar2i) in definition
• Key Idea: If output is a pair-type, result is often (hexp1i, hexp2i)
Fer-Jan de Vries Leicester, February 16, 2017
34
New Types from Old II — Tuple Types and Expressions
• Motivation: Some data consists of more than two parts
• Example: Person on a mailing list
– Specified by name, telephone number, and age
– A person p on the list can have type (String, Int, Int)
• Idea: Generalise pairs of types to collections of types
• Type Rule: Given types a1,…,an, then (a1,…,an) is a type
• Expression Formation: Given expressions e1::a1, …, en::an,
then
(e1,…,en) :: (a1,…,an)
Fer-Jan de Vries Leicester, February 16, 2017
35
Functions using Tuples
• Example 1: Write a function to test if a customer is an adult
isAdult :: (String,Int,Int) -> Bool
isAdult (name, tel, age) = (age >= 18)
• Example 2: Write a function to update the telephone number
updateMove :: (String,Int,Int) -> Int -> (String,Int,Int)
• Example 3: Write a function to update age after a birthday
updateAge :: (String,Int,Int) -> (String,Int,Int)
Fer-Jan de Vries Leicester, February 16, 2017
36
General Definition of a Function: Patterns with Tuples
• Definition: Functions now have the form
• Patterns: Patterns are
– Variables x: Use for any type
– Constants 0, True, ‘‘cherry’’: Definition by cases
– Tuples (x,..,z): If the argument has a tuple-type
– Wildcards : If the output doesn’t use the input
• In general: Use several lines and mix patterns.
Fer-Jan de Vries Leicester, February 16, 2017
37
More Examples
• Example: Using values and wildcards
isZero :: Int -> Bool
isZero 0 = True
isZero = False
• Example: Using tuples and multiple arguments
expand :: Int -> (Int,Int) -> (Int,Int,Int)
expand n (x,y) = (n, n*x, n*y)
• Example: Days in the month
days :: String -> Int -> Int
days “January” year = 31
days “February” year = if isLeap year then 29 else 28
days “March” year = 31
…..
Fer-Jan de Vries Leicester, February 16, 2017
38
New Types from Old III — Type Synonyms
• Motivation: More descriptive names for particular types.
• Definition: Type synonyms are declared with the keyword type.
type Team = String
type Goals = Int
type Match = ((Team,Goals), (Team,Goals))
numu :: Match
numu = ((“Newcastle”, 4), (“Manchester Utd”, 3))
• Functions: Types of functions are more descriptive, same code
homeTeam :: Match -> Team
totalGoals :: Match -> Goals
Fer-Jan de Vries Leicester, February 16, 2017
39
Today You Should Have Learned
• Tuples: Collections of data from other types
• Pairs: Pairs, triples etc are examples of tuples
• Type synonyms: Make programs easier to understand
• Pattern Matching: General description of functions covering
definition by cases, tuples etc.
• Pitfall! What is the di↵erence between
addPair :: (Int,Int) -> Int
addPair (x,y) = x + y
addTwo :: Int -> Int -> Int
addTwo x y = x + y
Fer-Jan de Vries Leicester, February 16, 2017
40
Lecture 4 — List Types
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
41
Overview of Lecture 4 — List Types
• Lists: What are lists?
– Forming list types
– Forming elements of list types
• Functions over lists: Some old friends, some new friends
– Functions from CO1003/4: cons, append, head, tail
– Some new functions: map, filter
• Clarity: Unlike Java, Haskell treatment of lists is clear
– No list iterators!
Fer-Jan de Vries Leicester, February 16, 2017
42
List Types and Expressions
• Example 1: [3, 5, 14] :: [Int] and [3, 4+1, double 7] :: [Int]
• Example 3: [’d’,’t’,’g’] :: [Char]
• Example 4: [[’d’], [’d’,’t’], [’d’,’t’,’g’]] :: [[Char]]
• Example 5: [double, square, cube] :: [Int -> Int]
• Empty List: The empty list is [] and belongs to all list types
• List Expressions: Lists are written using square brackets […]
– If e1, . . . , en are expressions of type a
– Then [e1, …, en] is an expression of type [a]
Fer-Jan de Vries Leicester, February 16, 2017
43
Some built in functions – Two infix operators
• Cons: The cons function : adds an element to a list
: :: a -> [a] -> [a]
1 : [2,3,4] = [1,2,3,4]
addone : [square] = [addone, square]
’a’ : [’b’, ’z’] = [’a’, ’b’, ’z’]
• Append: Append joins two lists together
++ :: [a] -> [a] -> [a]
[True, True] ++ [False] = [True, True, False]
[1,2] ++ ([3] ++ [4,5]) = [1,2,3,4,5]
([1,2] ++ [3]) ++ [4,5] = [1,2,3,4,5]
[] ++ [54.6, 67.5] = [54.6, 67.5]
[6,5] ++ (4 : [7,3]) = [6,5,4,7,3]
Fer-Jan de Vries Leicester, February 16, 2017
44
More Built In Functions
• Head and Tail: Head gives the first element of a list, tail the
remainder
head [double, square] = double
head ([5,6]++[6,7]) = 5
tail [double, square] = [square]
tail ([5,6]++[6,7]) = [6,6,7]
• Length and Sum: The length of a list and the sum of a list
of integers
length (tail [1,2,3]) = 2
sum [1+4,8,45] = 58
• Sequences: The list of integers from 1 to n is written
[1 .. n]
Fer-Jan de Vries Leicester, February 16, 2017
45
String is actually a list type
• Note: The type String is a type synonym for [Char].
• Hence we can use list notation on strings: eg.
•
head “abcdef” = ’a’
tail “abcdef” = “bcdef”
“Fer”++”-Jan” = “Fer-Jan”
“abcd”= ’a’:”bcd”
Fer-Jan de Vries Leicester, February 16, 2017
46
Two New Functions — Map And Filter
• Map: Map is a function which has two inputs.
– The first input is a function eg f
– The second is a list eg [e1,e1,e3]
The output is the list obtained by applying the function to every
element of the input list eg [f e1, f e2, f e3]
• Filter: Filter is a function which has two inputs.
– The first is a test, ie a function returning a Bool.
– The second is a list
The output is the list of elements of the input list which the
function maps to True, ie those elements which pass the test.
Fer-Jan de Vries Leicester, February 16, 2017
47
Using Map and Filter
• Even Numbers: The even numbers less than or equal to n
– evens::Int->[Int]
• Solution 1 — Using filter.
evens2 :: Int -> [Int]
evens2 n = filter isEven [1 .. n]
where isEven x = (x ‘mod‘ 2 == 0)
• Solution 2 — Using map
Fer-Jan de Vries Leicester, February 16, 2017
48
Today You Should Have Learned
• Types: We have looked at list types
– What list types and list expressions looks like
– What built in functions are available
• New Functions:
– Map: Apply a function to every member of a list
– Filter: Delete those that don’t satisfy a property or test
• Algorithms: Develop an algorithm by asking
– Can I solve this problem by applying a function to every
member of a list or by deleting certain elements.
Fer-Jan de Vries Leicester, February 16, 2017
49
Lecture 5 — List Comprehensions
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
50
Overview of Lecture 5
• Recall Map: Map is a function which has two inputs.
map add2 [2, 5, 6] = [4, 7, 8]
• Recall Filter: Filter is a function which has two inputs.
filter isEven [2, 3, 4, 5, 6, 7] = [2, 4, 6]
Both Map and Filter essentially construct new lists from old
lists.
• List comprehension: An alternative way of constructing lists
– Definition of list comprehension
– Comparison with map and filter
Fer-Jan de Vries Leicester, February 16, 2017
51
List Comprehension — An alternative to map and filter
• Example 1: If xs = [2,4,7] then
[ 2*x | x <- xs ] = [4,8,14] • Example 2: If isEven :: Int->Bool tests for even-ness
[ isEven x | x <- xs ] = [True,True,False] • In General: (Simple) list comprehensions are of the form [ hexpi | hvariablei <- hlist-expi ] • Evaluation: The meaning of a list comprehension is – Take each element of list-exp, evaluate the expression exp for each element and return the results in a list. Fer-Jan de Vries Leicester, February 16, 2017 52 Using List Comprehensions Instead of map • Example 1: A function which doubles a list’s elements double :: [Int] -> [Int]
• Example 2: A function which tags an integer with its evenness
isEvenList :: [Int] -> [(Int,Bool)]
• Example 3: A function to add pairs of numbers
addpairs :: [(Int,Int)] -> [Int]
• In general: map f l = [f x | x <- l]
Fer-Jan de Vries Leicester, February 16, 2017
53
Using List Comprehensions Instead of Filter
• Intuition: List Comprehension can also select elements from a
list
• Example: We can select the even numbers in a list
[ e | e <- l, isEven e]
• Example: Selecting names beginning with A
names :: [String] -> [String]
names l = [ e | e <- l , head e == ’A’ ]
• Example: Combining selection and applying functions
doubleEven :: [Int] -> [Int]
doubleEven l =[ 2*e | e <- l , isEven e ]
Fer-Jan de Vries Leicester, February 16, 2017
54
General Form of List Comprehension
• In General: These list comprehensions are of the form
[ hexpi | hvariablei <- hlist-expi , htesti ]
• Example: In fact, we can use several tests — if l = [2,5,8,10]
[ 2*e | e <- l , isEven e , e>3 ] = [16,20]
• Key Example: Cartesian product of two lists is a list of all
pairs, such that for each pair, the first component comes from
the first list and the second component from the second list.
[ (x,y) | x<-[1,2,3], y<-[’a’,’b’,’c’] ] = [(1,’a’), (1,’b’) ... ] league :: [Team] games = [ (t1,t2) | t1 <- league, t2 <- league, t1 /= t2] Fer-Jan de Vries Leicester, February 16, 2017 55 Removing Duplicates • Problem: Given a list remove all duplicate entries • Algorithm: Given a list, – Keep first element – Delete all occurrences of the first element – Repeat the process on the tail • Code: Fer-Jan de Vries Leicester, February 16, 2017 56 Today You Should Have Learned • List Types: We have looked at list types – What list types and list expressions looks like – What built in functions are available • List comprehensions: Like filter and map. They allow us to – Select elements of a list – Delete those that dont satisfy certain properties – Apply a function to each element of the remainder Fer-Jan de Vries Leicester, February 16, 2017 57 Lecture 6 — Recursion over Natural Numbers Fer-Jan de Vries Department of Computer Science University of Leicester February 16, 2017 58 Overview of Lecture 6 • Recursion: General features of recursion – What is a recursive function? – How do we write recursive functions? – How do we evaluate recursive functions? • Recursion over Natural Numbers: Special features – How can we guarantee evaluation works? – Recursion using patterns. – Avoiding negative input. Fer-Jan de Vries Leicester, February 16, 2017 59 What is recursion? • Example: Adding up the first n squares hssquares n = 12 + ... + (n-1)2 + n2 • Types: First we give the type of summing-squares hssquares :: Int -> Int
• Definitions: Our program is a function
hssquares 0 = 0
hssquares n = n*n + hssquares (n-1)
• Key Idea: hssquares is recursive as its definition contains hssquares
in a right-hand side – the function name “recurs”.
Fer-Jan de Vries Leicester, February 16, 2017
60
General Definitions
• Definition: A function is recursive if the name recurs in its
definition.
• Intuition: You will have seen recursion in action before
– Imperative procedures which call themselves
– Divide-and-conquer algorithms
• Why Recursion: Recursive definitions tend to be
– Shorter, more understandable and easier to prove correct
– Compare with a non-recursive solution
nrssquares n = n * (n+0.5) * (n+1)/3
Fer-Jan de Vries Leicester, February 16, 2017
61
Examples of evaluation
• Example 1: Let’s calculate hssquares 4
hssquares 4 ) 4*4 + hssquares 3
) 16 + (3*3 + hssquares 2)
. . .
) 16 + (9 + .. (1 + hssquares 0))
) 16 + (9 + … (1 + 0)) ) 30
• Example 2: Here is a non-terminating function
mydouble n = n + mydouble (n/2)
mydouble 4 ) 4 + mydouble 2
) 4 + 2 + mydouble 1
) 4 + 2 + 1 + mydouble 0.5 ) ……
• Question: Will evaluation stop?
Fer-Jan de Vries Leicester, February 16, 2017
62
Problems with Recursion
• Questions: There are some outstanding problems
1. Is hssquares defined for every number?
2. Does an evaluation of a recursive function always terminate?
3. What happens if hssquares is applied to a negative number?
4. Are these recursive definitions sensible: f n = f n, g n = g (n+1)
• Answers: Here are the answers
1. Yes: The variable pattern matches every input.
2. Not always: See examples.
3. Trouble: Evaluation doesn’t terminate.
4. No: Why not?
Fer-Jan de Vries Leicester, February 16, 2017
63
Primitive Recursion over Natural Numbers
• Motivation: Restrict definitions to get better behaviour
• Idea: Many functions defined by three cases
– A non-recursive call selected by the pattern 0
– A recursive call selected by the pattern n
– The error case deals with negative input
• Example Our program now looks like
hssquares2 0 = 0
hssquares2 n
| n>0 = n*n + hssquares (n-1)
| n<0 = if n < 0 error ‘‘Negative input’’
Fer-Jan de Vries Leicester, February 16, 2017
64
Examples of recursive functions
• Example 1: star uses recursion over Int to return a string
star :: Int -> String
star 0 = []
star n
| n>0 = ’*’ : star (n-1)
| n<0 = error ‘‘Negative input’’
• Example 2: power is recursive in its second argument
power :: Float -> Int -> Float
power x 0 = 1
power x n
| n>0 = x * power x (n-1)
| n<0 = error ‘‘Negative input’’
Fer-Jan de Vries Leicester, February 16, 2017
65
Primitive Recursion
• In General: Use the following style of definition
hfunction-namei 0 = hexp 1i
hfunction-namei n
| n>0 = hexp 2i
| n<0 = errorhmessagei
where
hexp 1i does not contain hfunction-namei
hexp 2i may contain hfunction-namei applied to n
• Evaluation: Termination guaranteed!
– If the input evaluates to 0, evaluate hexp 1i
– If not, if the input is greater than 0, evaluate hexp 2i
– If neither, return the error message
Fer-Jan de Vries Leicester, February 16, 2017
66
Larger Example
• Problem: Produce a table for perf :: Int -> (String, Int)
where perf 1 = (“Arsenal”,4) etc.
• Stage 1: We need some headings and then the actual table
printTable :: Int -> IO()
printTable numberTeams = putStr(header ++ rows numberTeams)
where
header = “Team\t Points\n”
• Stage 2: Convert each “row” to a string, recursively.
rows :: Int -> String
rows 0 = …..
rows n
| n >0 = …..
| n <0 = .....
67
Fer-Jan de Vries Leicester, February 16, 2017
The Function rows
• Base Case: If we want no entries, then just return []
rows 0 = []
• Recursive Case: Convert n-rows by
– recursively converting the first n-1-rows, and
– adding on the n-th row
• Code: Code for the recursive call
Fer-Jan de Vries Leicester, February 16, 2017
68
The Final Version
perf :: Int -> (String,Int)
perf 1 = (“Arsenal”,4)
perf 2 = (“Notts”,5)
perf 3 = (“Chelsea”,7)
perf n = error “perf out of range”
rows :: Int -> String
rows n
| n=0 = []
| n>1 = rows (n-1) ++ fst(perf n) ++ “\t\t ”
++ show(snd(perf n)) ++ “\n”
| n < 0 = error"rows out of range"
printTable :: Int -> IO()
printTable numberTeams = putStr(header ++ rows numberTeams)
where
header = “Team\t\t Points\n”
Fer-Jan de Vries Leicester, February 16, 2017
69
Today You Should Have Learned
• Recursion: Allows new functions to be written.
– Advantages: Clarity, brevity, tractability
– Disadvantages: Evaluation may not stop
• Primitive Recursion: Avoids bad behaviour of some recursive
functions
– The value at 0 is non-recursive
– Each recursive call uses a smaller input
– An error-clause catches negative inputs
• Algorithm: Ask yourself, what needs to be done to the recur-
sive call to get the answer.
Fer-Jan de Vries Leicester, February 16, 2017
70
Lecture 7 — Recursion over Lists
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
71
Overview of Lecture 7
• Lists: Another look at lists
– Lists are a recursive structure
– Every list can be formed by [] and :
• List Recursion: Primitive recursion for Lists
– How do we write primitive recursive functions
– Examples — ++, length, head, tail, take, drop, zip
• Avoiding Recursion?: List comprehensions revisited
Fer-Jan de Vries Leicester, February 16, 2017
72
Recursion over lists
• Question: This lecture is about the following question
– We know what a recursive function over Int is
– What is a recursive function over lists?
• Answer: In general, the answer is the same as before
– A recursive function mentions itself in its definition
– Evaluating the function may reintroduce the function
– Hopefully this will stop at the answer
Fer-Jan de Vries Leicester, February 16, 2017
73
Another Look at Lists
• Recall: The two basic operations concerning lists
– The empty list []
– The cons operator (:) :: a -> [a] -> [a]
• Key Idea: Every list is either empty, or of the form x:xs
[2,3,7] = 2:3:7:[] [True, False] = True:False:[]
• Recursion: Define recursive functions using the scheme
– Non-recursive call: Define the function on the empty list []
– Recursive call: Define the function on (x:xs) by using the
function only on xs
Fer-Jan de Vries Leicester, February 16, 2017
74
Examples of Recursive Functions
• Example 1: Doubling every element of an integer list
double :: [Int] -> [Int]
double [] = []
double (x:xs) = (2*x) : double xs
• Example 2: Selecting the even members of a list
onlyEvens :: [Int] -> [Int]
onlyEvens [] = []
onlyEvens (x:xs) = if isEven x then x:rest else rest
where rest = onlyEvens xs
• Example 3: Flattening some lists
flatten :: [[a]] -> [a]
flatten [] = []
flatten (x:xs) = x ++ flatten xs
Fer-Jan de Vries Leicester, February 16, 2017
75
The General Pattern
• Definition: Primitive Recursive List Functions are given by
hfunction-namei [] = hexpression 1i
hfunction-namei (x:xs) = hexpression 2i
where
hexpression 1i does not contain hfunction-namei
hexpression 2i may contain expressions hfunction-namei xs
• Compare: Very similar to recursion over Int
hfunction-namei 0 = hexpression 1i
hfunction-namei (n+1) = hexpression 2i
where
hexpression 1i does not contain hfunction-namei
hexpression 2i may contain expressions hfunction-namei n
Fer-Jan de Vries Leicester, February 16, 2017
76
More Examples:
• Example 4: Append is defined recursively
append :: [a] -> [a] -> [a]
• Example 5: Testing if an integer is an element of a list
member :: Int -> [Int] -> Bool
• Example 6: Reversing a list
reverse :: [a] -> [a]
Fer-Jan de Vries Leicester, February 16, 2017
77
What can we do with a list?
• Mapping: Applying a function to every member of the list
double [2,3,72,1] = [2*2, 2*3, 2*72, 2*1]
isEven [2,3,72,1] = [True, False, True, False]
• Filtering: Selecting particular elements
onlyEvens [2,3,72,1] = [2,72]
• Taking Lists Apart: head, tail, take, drop
• Combining Lists: zip
• Folding: Combining the elements of the list
sumList [2,3,7,2,1] = 2 + 3 + 7 + 2 + 1
Fer-Jan de Vries Leicester, February 16, 2017
78
List Comprehension Revisited OP
• Recall: List comprehensions look like
[ hexpi | hvariablei <- hlist-expi , htesti ]
• Intuition: Roughly speaking this means
– Take each element of the list hlist-expi
– Check they satisfy htesti
– Form a list by applying hexpi to those that do
• Idea: Equivalent to filtering and then mapping. As these are
recursive, so are list comprehensions although the recursion is
hidden
Fer-Jan de Vries Leicester, February 16, 2017
79
Today You Should Have Learned
• List Recursion: Lists are recursive data structures
– Hence, functions over lists tend to be recursive
– But, as before, general recursion is badly behaved
• Primitive List Recursion: Similar to natural numbers
– A non-recursive call using the pattern []
– A recursive call using the pattern (x:xs)
• List comprehension: An alternative way of doing some recur-
sion
Fer-Jan de Vries Leicester, February 16, 2017
80
Lecture 8 — More Complex Recursion
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
81
Overview of Lecture 8
• Problem: Our restrictions on recursive functions are too severe
• Solution: New definitional formats which keep termination
– Using new patterns
– Generalising the recursion scheme
• Examples: Applications to integers and lists
• Sorting Algorithms: What is a sorting algorithm?
– Insertion Sort, Quicksort and Mergesort
Fer-Jan de Vries Leicester, February 16, 2017
82
More general forms of primitive recursion
• Recall: Our primitive recursive functions follow the scheme
– Base Case: Define the function non-recursively at 0
– Inductive Case: Define the function at positive n in terms
of the function at (n-1)
hfunction-namei 0= hexp 1i
hfunction-namei n
| n>0 = hexp 2i
| n<0 = errorhmessagei
where
hexpression 1i does not contain hfunction-namei
hexpression 2i may contain hfunction-namei applied to n
• Motivation: But some functions do not fit this scheme, requir-
ing more complex patterns
83
Fer-Jan de Vries Leicester, February 16, 2017
Fibonacci Numbers – More Complex Patterns
• Example: The first Fibonacci numbers are 0,1. For each sub-
sequent Fibonacci number, add the previous two together
0, 1, 1, 2, 3, 5, 8, 13, 21, 34
• Problem: The following does not terminate on input 1
fib 0 = 0
fib n = fib (n-1) + fib (n-2)
• Solution: Second base case!
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
To be more precise add error message in case (n < 0). Other-
wise the funtion will not terminate at negative input.
Fer-Jan de Vries Leicester, February 16, 2017
84
More general recursion on lists
• Recall: Our primitive recursive functions follow the pattern
– Base Case: Defines the function at [] non-recursively
– Inductive Case: Defines the function at (x:xs) in terms of
the function at xs
hfunction-namei [] = hexp 1i
hfunction-namei (x:xs) = hexp 2i
where
hexpression 1i does not contain hfunction-namei
hexpression 2i may contain hfunction-namei applied to xs
• Motivation: As with integers, some functions don’t fit this
shape
Fer-Jan de Vries Leicester, February 16, 2017
85
More General Patterns for Lists
• Recall: With integers, we used more general patterns.
• Idea: Use (x:(y:xs)) pattern to access first two elements
• Example: We want a function to delete every second element
delete [2,3,5,7,9,5,7] = [2,5,9,7]
• Solution: Here is the code
delete :: [a] -> [a]
delete [] = []
delete [x] = [x]
delete (x:(y:xs)) = x : delete xs
• Example: To delete every third element use pattern (x:(y:(z:xs)))
Fer-Jan de Vries Leicester, February 16, 2017
86
Examples of Recursion and patterns — See how the typing helps
• Example 1: Summing pairs in a list of pairs
sumPairs :: [(Int,Int)] -> Int
• Example 2: Unzipping lists unZip :: [(a,b)] -> ([a],[b])
Fer-Jan de Vries Leicester, February 16, 2017
87
Sorting Algorithms 1: Insertsort
• Problem: A sorting algorithm rearranges a list in order
sort [2,7,13,5,0,4] = [0,2,4,5,7,13]
• Recursion: Such algorithms usually recursively sort a smaller
list
• Insertsort Alg: To sort a list, sort the tail recursively, and then
insert the head
• Code:
inssort :: [Int] -> [Int]
inssort [] = []
inssort (x:xs) = insert x (inssort xs)
where insert puts the number x in the correct place
Fer-Jan de Vries Leicester, February 16, 2017
88
The function insert
• Patterns: Insert takes two arguments, number and list
– The recursion for insert doesn’t depend on the number
– The recursion for insert does depend on whether the list is
empty or not — use the [] and (x:xs) patterns
• Code: Here is the final code
insert :: Int -> [Int] -> [Int]
insert n [] = [n]
insert n (x:xs)
| n <= x = n:x:xs
| otherwise = x:(insert n xs)
Fer-Jan de Vries Leicester, February 16, 2017
89
Sorting Algorithms 2: Quicksort
• Quicksort Alg: Given a list l and a number n in the list
sort l = sort those elements less than n ++
number of occurrences of n ++
sort those elements greater than n
• Code: The algorithm may be coded
qsort :: [Int] -> [Int]
qsort [] = []
qsort (x:xs) = qsort (less x xs) ++
occs x (x:xs) ++
qsort (more x xs)
where less, occs, more are auxiliary functions
Alternative: write x: occs x xs instead of occs x (x:xs)
Fer-Jan de Vries Leicester, February 16, 2017
90
Defining the Auxiliary Functions
• Problem: The auxiliary functions can be specified
– less takes a number and a list and returns those elements
of the list less than the number
– occs takes a number and a list and returns the occurrences
of the number in the list
– more takes a number and a list and returns those elements
of the list more than the number
• Code: Using list comprehensions gives short code
less, occs, more :: Int -> [Int] -> [Int]
less n xs = [x | x <- xs, x < n]
occs n xs = [x | x <- xs, x == n]
more n xs = [x | x <- xs, x > n]
Fer-Jan de Vries Leicester, February 16, 2017
91
Sorting Algorithm 3: Mergesort
• Mergesort Alg: Split the list in half, recursively sort each half
and merge the results
• Code: Overall function reflects the algorithm
msort [] = []
msort [x] = [x]
msort xs = merge (msort ys) (msort ws)
where (ys,ws) = (take l xs, drop l xs)
where l = length xs ‘div‘ 2
where merge combines two sorted lists
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) = if x
doubleList [] = []
doubleList (x:xs) = (2*x) : doubleList xs
• Example 2: A function to square the elements of a list
squareList :: [Int] -> [Int]
squareList [] = []
squareList (x:xs) = (x*x) : squareList xs
• Example 3: A function to increment the elements of a list
incList :: [Int] -> [Int]
incList [] = []
incList (x:xs) = (x+1) : incList xs
Fer-Jan de Vries Leicester, February 16, 2017
96
The Common Pattern
• Problem: Three separate definitions despite a clear pattern
• Intuition: Examples apply a function to each member of a list
function :: Int -> Int
functionList :: [Int] -> [Int]
functionList [] = []
functionList (x:xs) = (function x) : functionList xs
where in our previous examples function is
double square inc
• Key Idea: Make auxiliary function function an input
Fer-Jan de Vries Leicester, February 16, 2017
97
A Higher Order Function — map
• The Idea Coded:
map f [] = []
map f (x:xs) = (fx) : map f xs
• Advantages: There are several advantages
– Shortens code as previous examples are given by
doubleList xs = map double xs
squareList xs = map square xs
incList xs = map inc xs
– Captures the algorithmic content and is easier to understand
– Easier code-modification and code re-use
Fer-Jan de Vries Leicester, February 16, 2017
98
A Definition of Higher Order Functions
• Question: What is the type of map?
– First argument is a function
– Second argument is a list whose elements have the same
type and the input of the function.
– Result is a list whose elements are the output type of the
function.
• Answer: So overall type is map :: (a -> b) -> [a] -> [b]
• Definition: A function is higher-order if an input is a function.
• Another Example: Type of filter is
filter :: (a -> Bool) -> [a] -> [a]
Fer-Jan de Vries Leicester, February 16, 2017
99
Quicksort Revisited
• Idea: Recall our implementation of quicksort
qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort less ++ occs ++ qsort more
where
less = [e | e<-xs, e
• Polymorphism: Quicksort requires an order on the elements:
– The output list depends upon the order on the elements
– This requirement is reflected in type class information Ord a
– Don’t worry about type classes as they are beyond this course
Fer-Jan de Vries Leicester, February 16, 2017
100
Limitations of Quicksort
• Example: Games tables might have type [(Team,Points)]
• Problem: How can we order the table?
Arsenal 16
AVilla 16
Derby 10
Birm. 4
…
• Solution: Write a new function for this problem
tSort [] = []
tSort (x:xs) = tSort less ++ [x] ++ tSort more
where more = [e| e<-xs, snd e > snd x]
less = [e| e<-xs, snd e < snd x]
• What did we assume here?
Fer-Jan de Vries Leicester, February 16, 2017
101
Higher Order Sorting
• Motivation: But what if we want other orders, eg
– Sort teams in order of names, not points
– Sort on points, but if two teams have the same points, com-
pare names
• Key Idea: Make the comparison a parameter of quicksort
qsortCp :: (a -> a -> Bool) -> [a] -> [a]
qsortCp ord [] = []
qsortCp ord (x:xs) = qsortCp ord less ++ occs ++ qsortCp ord more
where less = [ e | e <- xs, ord e x]
occs = x : [ e | e <- xs, e == x]
more = [ e | e <- xs, ord x e]
Fer-Jan de Vries Leicester, February 16, 2017
102
Examples
• Key Idea: To use a higher order sorting algorithm, use the
required order to define the function to sort by
• Example 1: To sort by names
ord (t, p) (t’, p’) = t < t’
• Example 2: To sort by points and then names
ord (t, p) (t’, p’) = (p < p’) || (p == p’ && t < t’)
• What should we assume about ord?
Fer-Jan de Vries Leicester, February 16, 2017
103
Today You Should Have Learned
• Higher Order Functions: Functions which takes functions as
input
– Facilitates code reuse and more abstract code
– Many list functions are either map, filter or fold
• HO Sorting: An application of higher order functions to sorting
– Produces more powerful sorting
– Order of resulting list determined by a function
– Lexicographic order allows us to try one order and then an-
other
Fer-Jan de Vries Leicester, February 16, 2017
104
Lecture 10 — (Parametric) Polymorphism
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
105
Overview of Lecture 10
• Motivation: Some examples leading to polymorphism
• Definition: What is parametric polymorphism?
– What is a polymorphic type?
– What is a polymorphic function?
– Polymorphism and higher order functions
– Applying polymorphic functions to polymorphic expressions
Fer-Jan de Vries Leicester, February 16, 2017
106
Monomorphic length
• Example: Let us define the length of a list of integers
mylength :: [Int] -> Int
mylength [] = 0
mylength (x:xs) = 1 + mylength xs
• Problem: We want to evaluate the length of a list of characters
Prelude> mylength [’a’, ’g’]
ERROR: Type error in application
*** expression : mylength [’a’,’g’]
*** term : [’a’,’g’]
*** type : [Char]
*** does not match : [Int]
• Solution: Define a new length function for lists of characters
. . . but this is not very e�cient!
Fer-Jan de Vries Leicester, February 16, 2017
107
Polymorphic length
• Better Solution: The algorithm’s input depends on the list
type, but not on the type of integers.
• Idea: An alternative approach to typing mylength
– There is one input and one output: mylength :: a -> b
– The output is an integer: mylength :: a -> Int
– The input is a list: mylength :: [c] -> Int
– There is nothing more to infer from the code of mylength so
mylength :: [c] -> Int
This is an e�cient function – works at all list types!
Fer-Jan de Vries Leicester, February 16, 2017
108
Haskell’s Polymorphic Type System
• Types: Now we will deal with the following types:
– Basic, built in types: Int, Char, Bool, String, Float
– Type variables representing any type: a, b, c, …
– Types built with type construc tors: [], ->, (,)
[Int] a->a a->b a->Bool (String,a->a) [a->Bool]
– Type synonyms: type
type Point = (Int,Int)
type Line = (Point,Point)
type Test = a->Bool
Fer-Jan de Vries Leicester, February 16, 2017
109
Some Definitions
• Polymorphism is the ability to appear in di↵erent forms
• Definition: A type is parametric polymorphic i↵ it contains
type variables (that is, type parameters).
• Definition: A function is parametric polymorphic i↵ it can be
called on di↵erent types of input, and it is implemented by (code
for) a single algorithm
• Definition: A function is overloaded i↵ it can be called on
di↵erent types of input, and for each type of input, the function
is implemented by (code for) a particular algorithm.
• Examples: Of overloading are the arithmetic operators: integer
and floating-point addition.
Fer-Jan de Vries Leicester, February 16, 2017
110
Polymorphic Expressions
• Key Idea: Expressions have many types
– Amongst these is a principle type
• Example: What is the type of id x = x
– id sends an integer to an integer. So id :: Int -> Int
– id sends a list of type a to a list of type a. So id::[a]->[a]
– id sends an expression of type b to an expression of type b.
So id::b->b
• Principle Type: The last type includes the previous two – why?
– In fact the principal type of id is id::b->b – why?
Fer-Jan de Vries Leicester, February 16, 2017
111
Examples
• Example 1: What is the type of map
map f [] = []
map f (x:xs) = f x : map f xs
• Example 2: What is the type of filter
filter f [] = []
filter f (x:xs) = if f x then x:filter f xs else filter f xs
• Example 3: What is the type of iterate
iterate f 0 x = x
iterate f (n+1) x = f (iterate f n x)
Fer-Jan de Vries Leicester, February 16, 2017
112
Applying Polymorphic Expressions to Polymorphic Functions
• Previously: The typing of applications of expressions:
– If exp1 is an expression with type a -> b
– And exp2 is an expression with type a
– Then exp1 exp2 has type b
• Problem: How does this apply to polymorphic functions?
length :: [c] -> Int
[2,4,5] :: [Int]
length [2,4,5] :: Int
• Key Idea: Argument type can be an instance of input type
Fer-Jan de Vries Leicester, February 16, 2017
113
When is a Type an Instance of Another Type
• Recall: Two facts about expressions containing variables
– Variables stand for arbitrary elements of a particular type
– Instances of the expression are obtained by substituting ex-
pressions for variables
• Key Idea: (Parametric) polymorphic types are defined in the
same way:
– Type-expressions may contain type-variables
– Instances of type-expressions are obtained by substituting
types for type-variables
• Example: [Int] is an instance of [c] – substitute Int for c
Fer-Jan de Vries Leicester, February 16, 2017
114
More formally – Unification OP
• Monomorphic: Can a function be applied to an argument?
– If the function’s input type is the same type as its argument
f::a->b x::a
f x :: b
• Polymorphically: Can a function be applied to an argument?
– If the function’s input type is unifiable with argument’s type
f::a->b x::c ✓ unifies a,c
f x :: ✓b
where ✓ maps type variables to types
• Example: In the length example, set ✓c=Int
Fer-Jan de Vries Leicester, February 16, 2017
115
Example
• Past Paper: Assume f is a function with principle type
f::([a],[b])->Int->[(b,a)]
Do the following expressions type check? State Yes or No with
a brief reason and, if Yes, what is the principal type of the
expression?
1. f (3,3) 2
2. f ([],[]) 5
3. f ([tail,head], []) 3
4. f ([True,False], [’x’])
Fer-Jan de Vries Leicester, February 16, 2017
116
Today You Should Have Learned
• Polymorphism:
– Saves on code — one function (algorithm) has many types
– This implements our algorithmic intuition
• Type Checking: Expressions and functions have many types
including a principle one
– Polymorphic functions are applied to expressions whose type
is an instance of the type of the input of the function
Fer-Jan de Vries Leicester, February 16, 2017
117
Lecture 10 — Algebraic Types
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
118
Recall the types in Haskell we have seen so far.
• basic types like Int , Float , Char , Bool
• compound types:
– tuple types like (Int, String)
– list types like [Int]
– function types like Int -> Int
– type synonyms like type Word = String
– polymorphic types like (a->b) -> [a] ->[b]
– polymorphic types and classes like Ord a => [a] -> [a]
Fer-Jan de Vries Leicester, February 16, 2017
119
There are other types which are di�cult to model using the types
seen so far.
Examples include:
• The type of months: January, February, .., December.
• The type of geometric shapes whose elements are either circles
or rectangles.
• The type of trees.
All these types can be modelled by Haskell algebraic types.
Fer-Jan de Vries Leicester, February 16, 2017
120
Algebraic Types I: Enumerated Types
Enumerated types are types which are defined by enumer-
ating the elements.
Example. Temperatures are either hot or cold.
data Temp = Cold | Hot
The type Temp has two members Cold and Hot, such that
Cold :: Temp
Hot :: Temp
Cold and Hot are called CONSTRUCTORS.
Fer-Jan de Vries Leicester, February 16, 2017
121
Enumerated Types
Example.
data Season = Spring | Summer | Autumn | Winter
The type Season has four members (four constructors) which are:
Spring, Summer, Autumn and Winter.
This means that
Spring :: Season
Summer :: Season
Autumm :: Season
Winter :: Season
Fer-Jan de Vries Leicester, February 16, 2017
122
Functions on enumerated types
data Month = January|February|March|April|May|June|July|
August|September|October|November|December
Example. Checking if a month is in summer.
isSummer :: Month -> Bool
isSummer June = True
isSummer July = True
isSummer August = True
isSummer September = True
isSummer = False
To define functions over enumerated types we use pattern matching.
Fer-Jan de Vries Leicester, February 16, 2017
123
General Form of Enumerated Types
ENUMERATED TYPES.
data htype-namei = hconstructor 1i | … | hconstructor ni
FUNCTIONS ON ENUMERATED TYPES.
hfunction-namei :: htype-namei� > hout-typei
hfunction-nameihpati = hexpi
Rule for names: the name of the type and the names of the con-
structors begin with capital letters. Name of functions begin with
lower case.
Fer-Jan de Vries Leicester, February 16, 2017
124
Algebraic types II: constructor functions
Example. A geometric shape is either a circle or a rectangle.
data Shape = Circle Float | Rectangle Float Float
There are two ways of building an element of Shape:
1. Supplying the radius of a circle:
Circle 3.9 :: Shape
2. Giving the sides of a rectangle:
Rectangle 3.5 13.5 :: Shape
Key idea. Incorporate extra type information in type definition.
Fer-Jan de Vries Leicester, February 16, 2017
125
Constructors functions
Example.
data Shape = Circle Float | Rectangle Float Float
Elements of type Shape are constructed by applying Shape and Circle
to certain arguments.
Circle :: Float -> Shape
Rectangle :: Float -> Float -> Shape
Circle and Shape are called
CONSTRUCTOR FUNCTIONS or just CONSTRUCTORS.
Fer-Jan de Vries Leicester, February 16, 2017
126
General form of algebraic types II
The general form of such a definition looks like
data htype-namei = hCons-1i htypei . . . htypei
| hCons-2i htypei . . . htypei
.. …
| hCons-ri htypei . . . htypei
Here the constructor (functions) are:
Cons-i :: type ! . . . ! type ! type-name
Fer-Jan de Vries Leicester, February 16, 2017
127
Functions over algebraic types II
Example. Testing if a shape is a rectangle
isRect :: Shape -> Bool
isRect (Circle x) = False
isRect (Rectangle h w) = True
Key Idea. Again, use pattern matching to define functions. Now,
supply variables for the constructor functions.
Fer-Jan de Vries Leicester, February 16, 2017
128
Functions over algebraic types II
Example. Area of a shape is given by
area :: Shape -> Float
area (Circle r) = pi * r * r
area (Rectangle h w) = h * w
Alternative definition:
area :: Shape -> Float
area x = case x of
Circle r -> pi * r * r
Rectangle h w -> h * w
Fer-Jan de Vries Leicester, February 16, 2017
129
Deriving functions of built-in classes
Example.
data Temp = Cold | Hot
PROBLEM. If you type Hot to the prompt, you get an error!!!
Main > Hot
ERROR – Cannot find “show” function for:
*** Expression : Hot
*** Of type : Temp
SOLUTION. Add the clause deriving after the type definition.
data Temp = Cold|Hot
deriving Show
What does deriving mean? What is Show?
Fer-Jan de Vries Leicester, February 16, 2017
130
Built-in classes and their functions
Class of types Operators defined over the types
belonging to the class
Eq equality and inequality
Ord order between elements
Enum operations like [n..m]
Show operations that turn elements into textual form
Fer-Jan de Vries Leicester, February 16, 2017
131
Deriving functions of built-in classes
If we want to define a non-standard equality on the type Temp, we
have to make the type an instance of the class Eq.
instance Eq Temp where
Cold == Cold = True
== = False
If we want the standard definition of equality, Haskell generates it
automatically.
data Temp = Cold|Hot
deriving Eq
Fer-Jan de Vries Leicester, February 16, 2017
132
Deriving functions of built-in classes
Example.
data Season = Spring | Summer | Autumn | Winter
deriving (Eq,Ord,Enum,Show)
Haskell automatically generates definitions of equality, ordering,
enumeration and text functions.
Spring == Spring evaluates to True.
[Summer .. Winter] gives the list [Summer, Autumn, Winter].
Fer-Jan de Vries Leicester, February 16, 2017
133
Deriving functions of built-in classes.
Example. Months of the year:
data Month = January|February|March|April|May|June|July|
August|September|October|November|December
deriving (Eq,Ord,Enum,Show)
Example.
data Shape = Circle Float | Rectangle Float Float
deriving (Eq,Ord,Show)
We cannot enumerate shapes. Being in Enum can only be derived
for enumerated types.
Fer-Jan de Vries Leicester, February 16, 2017
134
Today you should have learned
Algebraic Types. Algebraic types allow
• Choice in the sorts of elements that make up the type
• Elements can contain parameters which belong to other types
Functions.
1. Functions are defined by pattern matching.
2. Functions of built-in classes can be derived automatically by
Haskell.
Fer-Jan de Vries Leicester, February 16, 2017
135
Lecture 11 — Recursive Algebraic Types
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
136
Recursive algebraic types
Recursive algebraic types are types which are described in terms of
themselves.
Example. A type Exp for arithmetic expressions defined by
data Exp = Num Int | Add Exp Exp | Mul Exp Exp
deriving Show
Key Idea: Exp also appears on the righthand side of the definition.
Informal expression Haskell representation
5 Num 5
5+ 21 Add (Num 5) (Num 21)
8 ⇤ 10 Mul (Num 8) (Num 10)
(4 ⇤ (2 + 5)) Mul (Num 4) (Add (Num 2) (Num 15))
Fer-Jan de Vries Leicester, February 16, 2017
137
Elements of recursive types
Elements of Exp are either
1. integer expressions:
Num 5 :: Exp
2. or a combination of expressions using the arithmetic operations:
Add (Num 5) (Num 7) :: Exp
Mul (Num 5) (Num 7) :: Exp
To build an element of type Elem we use a combination of the
following three constructor functions:
Num :: Int -> Exp
Add :: Exp -> Exp -> Exp
Mul :: Exp -> Exp -> Exp
Fer-Jan de Vries Leicester, February 16, 2017
138
Recursive algebraic types
Example. Lists of integers can be modelled by the type:
data NList = Nil | Cons Int NList
deriving Show
Instances of elements of type NList are:
Nil
Cons 12 Nil
Cons 17 (Cons 12 Nil)
The constructors are:
Nil :: NList
Cons :: Int -> NList ->NList
Fer-Jan de Vries Leicester, February 16, 2017
139
Recursive algebraic types
Example. Trees of integers can be modelled by the type:
data NTree = NNil | NNode Int NTree NTree
deriving Show
Instances of elements of type NTree are:
NNil
NNode 12 NNil NNil
NNode 17 (NNode 12 NNil NNil) (NNode 22 NNil NNil)
The constructors are:
NNil :: NTree
NNode :: Int -> NTree -> NTree -> NTree
Fer-Jan de Vries Leicester, February 16, 2017
140
Functions over recursive algebraic types
Recall: functions over algebraic types are defined by pattern match-
ing with one clause for each constructor
data Shape = Circle Float | Rectangle Float Float
perim :: Shape -> Float
perim (Circle x) = 2 * 3.14 * x
perim (Rectangle h w) = 2 * (h + w)
Fer-Jan de Vries Leicester, February 16, 2017
141
Functions over recursive algebraic types
Example. Evaluation of expressions
eval :: Exp -> Int
eval (Num x) = x
eval (Add e1 e2) = eval e1 + eval e2
eval (Mul e1 e2) = eval e1 * eval e2
Now our functions can be recursive. The form of the recursive
definition follows the recursion on the type definition.
There are two parts:
1. Non-recursive or base cases. The value of Num x is x.
2. Recursive cases. The value of an expression is calculated in
terms of the values of its subexpresions e1 and e2.
Fer-Jan de Vries Leicester, February 16, 2017
142
Functions over recursive algebraic types
Example. Sum the nodes of a tree:
sumTree :: NTree -> Int
sumTree NNil = 0
sumTree (NNode n t1 t2) = n+ sumTree t1 + sumTree t2
Example. Left subtree:
leftTree :: NTree -> NTree
leftTree NNil = NNil
leftTree (NNode n t1 t2) = t1
This is not a recursive definition.
Fer-Jan de Vries Leicester, February 16, 2017
143
Functions over recursive algebraic types
Example. The depth of a tree:
depth:: NTree -> Int
depth NNil = 0
depth (NNode n t1 t2) = 1 + max (depth t1) (depth t2)
Example. Find out how many times a number p occurs in a tree:
occurs:: NTree -> Int -> Int
occurs NNil p = 0
occurs (NNode n t1 t2) p
|n==p = 1 + occurs t1 p + occurs t2 p
|otherwise = occurs t1 p + occurs t2 p
Fer-Jan de Vries Leicester, February 16, 2017
144
Today you should have learned
Types. Algebraic types can be recursive, eg trees can contain
subtrees.
Functions.
• As before, define functions on each constructor.
• There is a natural way of definining recursive functions on re-
cursive types.
Representations: How to represent elements of a data structure
as expressions of a Haskell datatype.
Fer-Jan de Vries Leicester, February 16, 2017
145
Lecture 12 —
Polymorphic Algebraic Types
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
146
Trees of integers, trees of strings, …
Example. Recall the definition of trees of integers:
data NTree = NNil | NNode Int NTree NTree
deriving Show
Example. Trees of strings can be modelled by the type:
data STree = SNil | SNode String STree STree
deriving Show
There is nothing special about numbers or strings. All these types
of trees have the same structure, shape or form. We will define
polymorphic trees.
Fer-Jan de Vries Leicester, February 16, 2017
147
Polymorphic algebraic types: polymorphic trees
Trees that carry elements of an arbitrary type at the nodes:
data Tree a = Nil | Node a (Tree a) (Tree a)
deriving Show
where a is a type variable, i.e. a ranges over types.
Key ideas: • The type Tree a is parametric on the type a.
• We have to instantiate a to get a particular type of trees:
a Tree a description of the type
Int Tree Int trees of integers
String Tree String trees of strings
[Int] Tree [Int] trees of lists of integers
• We get a family
of types: Tree Int, Tree String, etc.
Fer-Jan de Vries Leicester, February 16, 2017
148
Elements of polymorphic trees
Examples.
1. Nil :: Tree a for any type a
2. An element of Tree Int is:
Node 12 Nil Nil :: Tree Int
3. An element of Tree String is:
Node “Leicester” Nil Nil :: Tree String
4. An element of Tree [Int] is:
Node [1,2] (Node [2,5,1] Nil Nil) Nil :: Tree [Int]
Fer-Jan de Vries Leicester, February 16, 2017
149
Polymorphic algebraic types: polymorphic lists
The built-in type of lists can be given by the definition:
data List a = NilList | Cons a (List a)
deriving Show
where we use the following notation:
Haskell notation our definition
[a] List a
[] NilList
: Cons
The type [a] is parametric on a. By instantiating a, we get a family
of types: [Int], [String], [[Int]], etc.
Fer-Jan de Vries Leicester, February 16, 2017
150
Function on polymorphic algebraic types (1)
Example. The depth of a tree
depth:: Tree a -> Int
depth Nil = 0
depth (Node n t1 t2) = 1 + max (depth t1) (depth t2)
The function depth is polymorphic: it has a common definition for
all the family of trees.
Fer-Jan de Vries Leicester, February 16, 2017
151
Functions on polymorphic algebraic types (2)
Example. Find out how many times a number p occurs in a tree:
occurs:: Eq a => Tree a -> a -> Int
occurs Nil p = 0
occurs (Node n t1 t2) p
|n==p = 1 + occurs t1 p + occurs t2 p
|otherwise = occurs t1 p + occurs t2 p
The type of this polymorphic function is conditional: it has the
condition that the type a should belong to the class Eq.
Fer-Jan de Vries Leicester, February 16, 2017
152
Functions on polymorphic algebraic types (3)
Example. The function mapTree is similar to the function map over
lists:
mapTree :: (a->b) -> Tree a -> Tree b
mapTree f Nil = Nil
mapTree f (Node x t1 t2) = Node (f x) (mapTree f t1)
(mapTree f t2)
Key ideas.
• it is higher order, i.e. it is a function that has an argument f
which is a function too.
• it is polymorphic: it has a common definition with type (a->b) ->
Tree a -> Tree b for all types a and b.
Fer-Jan de Vries Leicester, February 16, 2017
153
What is a Path?
Example. Consider the tree:
Node 3 (Node 7 Nil Nil) (Node 2 (Node 3 Nil Nil) Nil)
What do we have to do to get to the second occurrence of 3?
First go to the right and then go to the left.
Type Definition: Encode paths as follows
• A direction is either left or right: data Dir = L | R
• A path is a list of directions: type Path = [Dir]
Example: So the path to the second occurrence of 3 is represented
by the list [R,L].
Fer-Jan de Vries Leicester, February 16, 2017
154
Functions with Paths
Example 1: Is a path valid for a particular tree?
isPath :: Path -> Tree a -> Bool
isPath [] (Node n t1 t2) = True
isPath (L:p) (Node n t1 t2) = isPath p t1
isPath (R:p) (Node n t1 t2) = isPath p t2
isPath = False
For instance, the path [L] is not a valid path for the tree Node 3
Nil (Node 2 Nil Nil) because there is nothing in the left subtree.
Fer-Jan de Vries Leicester, February 16, 2017
155
Functions with Paths
Example 2: What data is stored in a tree at the end of the path?
extract :: Path -> Tree a -> a
extract [] (Node n t1 t2) = n
extract (L:p) (Node n t1 t2) = extract p t1
extract (R:p) (Node n t1 t2) = extract p t2
extract = error “There is no data at this path”
We can extract the number 3 at the end of the path [R,L] from
Node 3 (Node 7 Nil Nil) (Node 2 (Node 3 Nil Nil) Nil)
Fer-Jan de Vries Leicester, February 16, 2017
156
Variants of Trees
Example. Trees which store data in all the nodes. A Tree is
• A leaf storing data
• A node storing a left subtree, some data, and a right subtree
data Tree1 a = Leaf a | Node1 (Tree1 a) a (Tree1 a)
Fer-Jan de Vries Leicester, February 16, 2017
157
Variants of Trees
Example. Trees which may store no data at a leaf
• The empty tree storing no data
• A leaf storing data
• A node storing a left subtree, some data, and a right subtree
data Tree2 a = ND | Leaf a | Node2 (Tree2 a) a (Tree2 a)
Key Idea: To define a type, first work out the constructors and
their types
Fer-Jan de Vries Leicester, February 16, 2017
158
Today you should have learned
Types: Algebraic types can be
• Polymorphic, eg trees can store di↵erent forms of data
• Polymorphic algebraic types have a type parameter, eg
Tree Int or Tree String, not Tree
Fer-Jan de Vries Leicester, February 16, 2017
159
Lecture 13 — Errors
Fer-Jan de Vries
Department of Computer Science
University of Leicester
February 16, 2017
160
Four approaches to handle errors
How should a program deal with a situation which ought not to
occur?
Examples: divide by zero, head of an empty list, etc.
We will discuss four approaches for handling errors:
1. The error function.
2. Dummy values.
3. Auxiliary functions and the error function.
4. The Error types (the nicest solution).
Fer-Jan de Vries Leicester, February 16, 2017
161
First approach: the error function (1)
The error function stops the computation and prints a message.
Example. Recall the cost function:
type NumCars = Int
type DailyCost = Float
cost :: NumCars -> DailyCost
cost n
| n < 0 = error "Car production is always positive"
| 0 <= n && n <= 1000 = 6*m + 1000
| otherwise = 4*m + 450
where m = fromIntegral n
Problem. We may loose useful information for stopping the com-
putation.
Fer-Jan de Vries Leicester, February 16, 2017
162
First approach: the error function (2)
Suppose we have a database with a daily record of the number of
cars produced by a factory:
recordcars = [ 1000, -25000, 230000, -20000, 45000, 30000]
map cost recordcars evaluates to
[7000.0,
Program error: Car production is always positive
Problem. The computation stops in the 2nd value and we loose
the rest of the information.
Fer-Jan de Vries Leicester, February 16, 2017
163
First approach: the error function (3)
Example. Recall this notion of trees:
data Tree2 a = ND | Leaf a | Node2 (Tree2 a) a (Tree2 a)
Adding an element at one ND
addData :: a -> Tree2 a -> Tree2 a
addData x ND = Leaf x
addData x (Leaf y) = error ‘‘Cannot add Data’’
addData x (Node2 t1 y t2) = Node2 (addData x t1) y (addData x t2)
Wrong! This doesn’t work, because it
• replaces all ND’s with the data
• crashes as soon as we hit a leaf
We want a mechanism that explains when the program worked al
right
Fer-Jan de Vries Leicester, February 16, 2017
164
Second approach: dummy values
Instead of giving an error message, we can choose to give a partic-
ular value (called dummy value) in the error case.
cost n
| n < 0 = -1 | 0 <= n && n <= 1000 = 6*m + 1000 | otherwise = 4*m + 450 where m = fromIntegral n This works if the cost is expected to be always positive. Drawback. In many cases there is no way to tell when an error has occurred. For instance, imagine a cost function that substracts 1000 instead of adding it. Fer-Jan de Vries Leicester, February 16, 2017 165 Third approach: an auxiliary function To solve the problem of adding data in a tree, we can define an auxiliary function. We first test whether there is some ND in the tree or not: isND :: Tree2 a -> Bool
isND ND = True
isND (Leaf x) = False
isND (Node2 t1 x t2) = (isND t1) || (isND t2)
Fer-Jan de Vries Leicester, February 16, 2017
166
Third approach: an auxiliary function
Then, we write a function that combines the auxiliary function and
the error function:
addData :: a -> Tree2 a -> Tree2 a
addData x ND = Leaf x
addData x (Leaf y) = error “There are no ND’s”
addData x (Node2 t1 y t2)
| (isND t1) = Node2 (addData x t1) y t2
| (isND t2) = Node2 t1 y (addData x t2)
| otherwise = error “There are no ND’s”
We will see a more e�cient way of solving this problem.
Fer-Jan de Vries Leicester, February 16, 2017
167
Final approach: error types
We return an error value as a result. For this, we define a polymor-
phic type:
data Err a = Ok a | Fail
We return a value that contains the following information:
• the program worked and returns a certain value of type a or
• the program didn’t work
Key Idea. Functions now return error types. Compare with Java’s
try�blocks
Fer-Jan de Vries Leicester, February 16, 2017
168
Final approach: error types
Example. Redefining cost
cost n
| n < 0 = Fail | 0 <= n && n <= 1000 = Ok(6*m + 1000) | otherwise = Ok(4*m + 450) where m = fromIntegral n Consider the database recordcars =[ 1000, -25000, 230000, -20000, 45000, 30000]. Now, map cost recordcars evaluates to [Ok 7000.0,Fail,Ok 920450.0,Fail,Ok 180450.0,Ok 120450.0]. Advantage. We can see all production costs: the correct and the incorrect ones. Fer-Jan de Vries Leicester, February 16, 2017 169 Final approach: error types Example. Remove the n-th element from a list: remove :: Int -> [a] -> Err [a]
remove 0 (x:xs) = Ok xs
remove (n+1) (x:xs) = case remove n xs of
Fail -> Fail
Ok zs -> Ok (x:zs)
remove n = Fail
Note the use of case. We have two cases: either remove n xs
evaluates to Fail or it evaluates to an expression of the form OK zs.
Fer-Jan de Vries Leicester, February 16, 2017
170
Final approach: error types
Example. Redefining addData e�ciently:
addData :: a -> Tree2 a -> Err (Tree2 a)
addData x ND = Ok (Leaf x)
addData x (Leaf y) = Fail
addData x (Node2 t1 y t2) = case (addData x t1) of
Ok t -> Ok (Node2 t y t2)
Fail -> case (addData x t2) of
Ok t -> Ok (Node2 t1 y t)
Fail -> Fail
Advantage. We do not need an auxiliary function like isND. The
test for ND’s is incorporated into the function addData. This version
of addData is more e�cient.
Fer-Jan de Vries Leicester, February 16, 2017
171
One for you …
• Example: What element occurs at a path in a tree
lookup :: Eq a => Path -> NTree a -> Err a
Fer-Jan de Vries Leicester, February 16, 2017
172
Today You Should Have Learned
• Trees: Di↵erent varieties of Trees
– Is there data stored in nodes?
– How many subtrees does a node have?
• Paths: Do you understand what paths are?
– Can you write functions using paths?
• Errors: Using error types as exception handlers
– Java has try, catch blocks etc
• Practical: Combines all three of these types
Fer-Jan de Vries Leicester, February 16, 2017
173