You are on page 1of 19

Functional Programming

Christopher D. Clack
2020

© 2020 Christopher D. Clack 1


FUNCTIONAL PROGRAMMING

Lecture 16
PROGRAMMING EXAMPLES

© 2020 Christopher D. Clack 2


This lecture provides three programming
FUNCTIONAL PROGRAMMING examples:
• A simple evaluator for an arithmetic
PROGRAMMING EXAMPLES expression,
• The peg-board game of solitaire, and
• Representing lists as functions

CONTENTS

• Simple evaluator for arithmetic expression

• The peg-board game of solitaire

• Representing lists as functions

© 2020 Christopher D. Clack 3


Our first example is a simple evaluator
FUNCTIONAL PROGRAMMING for arithmetic expressions, which
provides an example of using algebraic
PROGRAMMING EXAMPLES types and recursion
It demonstrates how algebraic types and
pattern matching give a good fit to
formal syntax definition
Problem specification: Write a Miranda
SIMPLE EVALUATOR FOR ARITHMETIC EXPRESSIONS program that takes as input a
representation of a simple arithmetic
expression and returns as output the
• Motivation: numeric value of that expression
• An example of using algebraic types and The grammar for such expressions is
given as shown here. Note that this is a
recursion formal grammar definition using Backus
Naur Form, it is not Miranda code.
• Specification:
• Write a Miranda program that takes a
representation of a simple arithmetic expression
and returns the value of that expression
• Represent the grammar:
exp :: const | exp op exp | ‘(‘ exp ‘)’
op :: ‘*’ | ‘+’ | ‘-’| ‘/’
const :: digit | digit const
digit :: ‘1’| ‘2’| ‘3’| ‘4’| ‘5’| ‘6’| ‘7’| ‘8’| ‘9’| ‘0’

© 2020 Christopher D. Clack 4


Note that the specification asks for a
FUNCTIONAL PROGRAMMING “representation” to be used, so we shall
• ignore lexical analysis,
PROGRAMMING EXAMPLES • assume parsing has been done, and
• just represent the syntax tree
We will choose an algebraic type to
represent the syntax tree, based on the
SIMPLE EVALUATOR FOR ARITHMETIC EXPRESSIONS grammar provided, using appropriate
constructors

• Specification asks for a “representation” to be used:


• ignore lexical analysis
• assume parsing has been done
• just represent the syntax tree

• Choose an algebraic type to represent the syntax tree,


based on the grammar provided, using appropriate
constructors

© 2020 Christopher D. Clack 5


This example code shows how the
FUNCTIONAL PROGRAMMING grammar (shown at the top) can be
represented almost directly as two
PROGRAMMING EXAMPLES algebraic types (shown at the bottom)

Notice how the previous BNF definitions


of const and digit are simply
implemented as the Miranda built-in
SIMPLE EVALUATOR FOR ARITHMETIC EXPRESSIONS type ``num’’

• Grammar provided:
exp :: const | exp op exp | ‘(‘ exp ‘)’
op :: ‘*’ | ‘+’ | ‘-’| ‘/’
const :: digit | digit const
digit :: ‘1’| ‘2’| ‘3’| ‘4’| ‘5’| ‘6’| ‘7’| ‘8’| ‘9’| ‘0’

• Algebraic types:

expression ::= Constant num


| App expression operator expression
| Bracketed expression

operator ::= Times | Plus | Minus | Divide

© 2020 Christopher D. Clack 6


Here are two test values that can be
FUNCTIONAL PROGRAMMING used to test our evaluator code, showing
how we create values of an algebraic
PROGRAMMING EXAMPLES type

SIMPLE EVALUATOR FOR ARITHMETIC EXPRESSIONS

• Test values:

||(4) ∗ 5
test1 = App (Bracketed (Constant 4)) Times (Constant 5)

||(4 + 5)∗(3−2)
test2 = App
(Bracketed (App (Constant 4) Plus (Constant 5)))
Times
(Bracketed (App (Constant 3) Minus (Constant 2)))

© 2020 Christopher D. Clack 7


And here we have the code for the main
FUNCTIONAL PROGRAMMING “eval” function and its subsidiary
“evalop” function
PROGRAMMING EXAMPLES The function eval takes an expression
and returns a number. It pattern-
matches on the legal values of type
expression – if it sees a constant it can
immediately return the value, if it sees a
SIMPLE EVALUATOR FOR ARITHMETIC EXPRESSIONS bracketed expression it recursively calls
itself on the expression inside the
brackets, but if it see the application of
eval :: expression −> num an operator to its two arguments then it
eval (Constant x) = x returns whatever is returned by the
eval (Bracketed e) = eval e application of evalop to three arguments
– the operator “op”, and the results of
eval (App e1 op e2) = evalop op (eval e1) (eval e2) recursive calls to eval on the two
arguments
evalop:: operator −> num −> num −> num The function evalop is relatively simple –
evalop Times x y = x * y it takes an operator and two numeric
arguments, performs the required
evalop Plus x y = x + y arithmetic and returns the result
evalop Minus x y = x – y At the bottom of the slide we see an
evalop Divide x y = x / y example application of eval to the test
data “test1”. Each evaluation step is
main = eval test1 provided, to illustrate how the function
→ eval (App (Bracketed (Constant 4)) Times (Constant 5)) “eval” works
→ evalop Times (eval (Bracketed (Constant 4))) (eval (Constant 5))
→ (eval (Bracketed (Constant 4))) * (eval (Constant 5))
→ (eval (Constant 4)) * (eval (Constant 5))
→ 4 * (eval (Constant 5))
→ 4 * 5
→ 20

© 2020 Christopher D. Clack 8


This is an example of the combined use
FUNCTIONAL PROGRAMMING of built-in types, algebraic types, and
higher order functions, applied at the
PROGRAMMING EXAMPLES level you have learned so far
Specification: write code to play the
peg-board game of “solitaire”. A
function “play” will take a list of moves
(where a peg can jump N/S/E/W over
A GAME OF SOLITAIRE another into a hole, and the jumped peg
is removed) and display the resulting
board after performing the moves in
• Motivation sequence. Invalid moves should give an
error
The game is played on a 7 x 7 board
• An example of the combined use of built-in where pegs can fit into holes in the
types, algebraic types, and higher order board. The 2 x2 squares in the four
corners are invalid positions – jumping
functions, at the level you have learned so far into or from these positions, or jumping
from or to outside the board, is not
allowed. The board starts with pegs in
• Specification all valid holes except the central hole
which is empty. The real game aims to
• Peg-board game of solitaire end with a single peg in the middle hole,
• Moves: a peg jumps (N/S/E/W) over another into but here we just print the board
a hole, and the jumped peg is removed
• Take a list of moves and display the resulting
board after performing the moves in sequence
• 7x7 board, pegs fit into holes
• some positions are invalid

© 2020 Christopher D. Clack 9


We start by defining algebraic types for
FUNCTIONAL PROGRAMMING peg and direction, and a type synonym
for board, which we choose to be a list
PROGRAMMING EXAMPLES of lists of peg
Then we bind the name initboard to the
initial layout for the board, using the
values Peg, Hole and Invalid, accordingly

A GAME OF SOLITAIRE

peg ::= Peg | Hole | Invalid

direction ::= N | S | E | W
board == [[peg]]

initboard :: board
initboard = [[Invalid, Invalid, Peg, Peg, Peg, Invalid, Invalid],
[Invalid, Invalid, Peg, Peg, Peg, Invalid, Invalid],
[ Peg, Peg, Peg, Peg, Peg, Peg, Peg],
[ Peg, Peg, Peg, Hole, Peg, Peg, Peg],
[ Peg, Peg, Peg, Peg, Peg, Peg, Peg],
[Invalid, Invalid, Peg, Peg, Peg, Invalid, Invalid],
[Invalid, Invalid, Peg, Peg, Peg, Invalid, Invalid]]

© 2020 Christopher D. Clack 10


The movements are given in a 3-tuple
FUNCTIONAL PROGRAMMING comprising two integers giving the x and
y coordinates of the peg to be moved
PROGRAMMING EXAMPLES (each 1..7 with the origin in the top left
corner) to specify a starting position,
and a character which takes one of the
values N, S, E, or W of type direction
(standing for North, South, East or West)
play :: [(num,num,direction)] -> [char] The function play applies foldl to the list
play moves of moves, using function f and initial
= display (foldl f initboard moves) position initboard. The result is of type
where board which is then turned into a [char]
f b move = (makemove.checkmove) (b, move) for displaying on the screen by the
checkmove (b,(x,y,m)) function display (which displays a peg as
= error "moved off board!", if ((y<3) & (m=N)) \/ ((y>5) & (m=S)) ‘O’ and anything else as ‘_’). Display
\/ ((x<3) & (m=W)) \/ ((x>5) & (m=E)) uses the library function lay
= error "no peg at given position", if (b!(y-1))!(x-1) ~= Peg foldl ensures that each move is applied
= error "no peg to jump over", if ((m=N) & (b!(y-2))!(x-1) ~= Peg)
in the correct sequence; each step is
\/ ((m=S) & (b!y)!(x-1) ~= Peg) performed by the function ”f” which
\/ ((m=E) & (b!(y-1))!x ~= Peg) takes two arguments (a board and a
\/ ((m=W) & (b!(y-1))!(x-2) ~= Peg)
move), creates a 2-tuple out of them
= error "no hole to jump into", if ((m=N) & (b!(y-3))!(x-1) ~= Hole) and then applies the function
\/ ((m=S) & (b!(y+1))!(x-1) ~= Hole) composition (makemove . checkmove)
\/ ((m=E) & (b!(y-1))!(x+1) ~= Hole) to that 2-tuple. Thus first the move is
\/ ((m=W) & (b!(y-1))!(x-3) ~= Hole) checked for validity and then the move
= (b,(x,y,m)), otherwise is made
makemove (b,(x,y,N)) = ((make Peg (x,y-2)).(make Hole (x,y-1)).(make Hole (x,y))) b
makemove (b,(x,y,S)) = ((make Peg (x,y+2)).(make Hole (x,y+1)).(make Hole (x,y))) b checkmove runs multiple tests based on
makemove (b,(x,y,E)) = ((make Peg (x+2,y)).(make Hole (x+1,y)).(make Hole (x,y))) b the position of the peg to be moved, the
makemove (b,(x,y,W)) = ((make Peg (x-2,y)).(make Hole (x-1,y)).(make Hole (x,y))) b move direction, whether there’s a peg to
make p (x,y) b = (take (y-1) b) ++ [(makerow p x (b!(y-1)))] ++ (drop y b) jump over and a hole to land in
makerow p x row = (take (x-1) row) ++ [p] ++ (drop x row) makemove creates a composition of
display b = lay (map (concat.(map g)) b) three moves to apply to the board, each
where of which uses the function “make”
g Peg = "O"
g Hole = "_" make takes apart the board, calling
g Invalid = "_" makerow to make the change to the
correct column in the correct row
© 2020 Christopher D. Clack 11
The final example is used to provide a
FUNCTIONAL PROGRAMMING better understanding of, and facility
with, higher order functions and
PROGRAMMING EXAMPLES currying
It also provides an example of how
structured data (such as a list) can be
implemented as a function (recalling
that Church numerals represent
REPRESENTING LISTS AS FUNCTIONS individual numbers as functions)

Motivation:

• Better understanding of, and facility with, higher order


functions and currying

• Example of how structured data can be implemented as


a function

© 2020 Christopher D. Clack 12


We have already seen how a list can be
FUNCTIONAL PROGRAMMING represented using an algebraic type:
mylist * ::= Nil | Cons * (mylist *)
PROGRAMMING EXAMPLES Thus, the list value (1 : (2 : (3 : []))) can
be represented as:
Cons 1 (Cons 2 (Cons 3 Nil))
The difference being that this data
REPRESENTING LISTS AS FUNCTIONS structure is now of type mylist num and
not of type [num]

We have already seen how a list can be represented using Now we consider a new representation
using functions that we will call cons and
an algebraic type: nil:
cons 1 (cons 2 (cons 3 nil))
mylist * ::= Nil | Cons * (mylist *) Notice the difference between Cons
with an uppercase C and cons with a
lowercase c: the former is a constructor
Thus, the list value (1 : (2 : (3 : []))) can be represented as: of an algebraic type whereas the latter
will be a function name. The same
observation applies to Nil and nil.
Cons 1 (Cons 2 (Cons 3 Nil))

The difference being that this data structure is now of type


mylist num and not of type [num]

Now we consider a new representation using functions that


we will call cons and nil:

cons 1 (cons 2 (cons 3 nil))

© 2020 Christopher D. Clack 13


Consider the following definitions for
FUNCTIONAL PROGRAMMING the two functions cons and nil:
cons a b f = f a b False
PROGRAMMING EXAMPLES nil f = f (error “head of nil”)
(error “tail of nil”)
True
We represent a list as either the
function name for nil:
REPRESENTING LISTS AS FUNCTIONS
list1 = nil

Consider the following definitions for the two functions or as a partial application of cons to its
first two arguments:
cons and nil: list2 = cons ‘A’ nil
Both list1 and list2 have the same type:
cons a b f = f a b False they are functions that take an
argument function f and return
whatever value is returned by f. The
nil f = f (error “head of nil”) (error “tail of nil”) True function f takes three arguments.

We represent a list as either the function name for nil:

list1 = nil

or as a partial application of cons to its first two arguments:

list2 = cons ‘A’ nil

© 2020 Christopher D. Clack 14


The final argument to cons and nil is
FUNCTIONAL PROGRAMMING only supplied when you want to do
something with a list – e.g. take its head
PROGRAMMING EXAMPLES item, or its tail, or ask if it is empty
Functions head, tail and isnil must each
take a list using this function
representation and then apply that list
function to another function that will
REPRESENTING LISTS AS FUNCTIONS take the required action
head x = x h
The final argument to cons and nil is only supplied when where
you want to do something with a list habc=a

head x = x h main = head (cons ‘A’ nil)


→ cons ‘A’ nil h
where → h ‘A’ nil False
habc=a → ‘A’
main = head (cons ‘A’ nil)
→ cons ‘A’ nil h
→ h ‘A’ nil False
→ ‘A’

© 2020 Christopher D. Clack 15


Here we repeat the code for the
FUNCTIONAL PROGRAMMING functions cons and nil and then give the
code for the functions head, tail and isnil
PROGRAMMING EXAMPLES Each function head, tail and isnil takes a
list argument (represented as a
function) and then applies that function
(that “list”) to an argument – and in
each case that argument is a function
REPRESENTING LISTS AS FUNCTIONS For head the function argument “h”
takes three arguments and returns the
first
cons a b f = f a b False For tail that function argument “t” takes
nil f = f (error “head of nil”) (error “tail of nil”) True three arguments and returns the second
For isnil that function argument “g”
takes three arguments and returns the
third
head x = x h
where
habc=a
tail x = x t
where
tabc=b
isnil x = x g
where
gabc=c

© 2020 Christopher D. Clack 16


Here’s an example evaluation of the
FUNCTIONAL PROGRAMMING head and tail functions applied to a
representation of a list containing the
PROGRAMMING EXAMPLES two numbers 1 and 2
The evaluations occur using Normal
order reduction, so first we apply head
to its argument – this evaluates to the
argument applied to the function h
REPRESENTING LISTS AS FUNCTIONS Looking again for the leftmost redex we
evaluate the leftmost occurrence of tail
applied to its argument – this reduces to
head (tail (tail (cons 1 (cons 2 nil)))) its argument applied to the function t
The next leftmost redex is tail applied to
→ (tail (tail (cons 1 (cons 2 nil)))) h an argument – which reduces to its
argument applied to the function t
→ ((tail (cons 1 (cons 2 nil))) t) h Again we look for the leftmost redex and
we find the function cons applied to two
→ (((cons 1 (cons 2 nil)) t) t) h arguments (whereas it takes three).
Remember that function application is
→((cons 1 (cons 2 nil) t) t) h left associative and if (f a b) is a partial
application then (f a b) c = f a b c, so the
→((t 1 (cons 2 nil) False) t) h leftmost redex is cons 1 (cons 2 nil) t
which reduces to t 1 (cons 2 nil) False,
→((cons 2 nil) t) h which reduces to cons 2 nil
→(t 2 nil False) h The next leftmost redex is cons 2 nil t
which reduces to t 2 nil False which is nil
→nil h The next leftmost redex is nil h which
reduces to h applied to two error
→h (error “head of nil”) (error “tail of nil”) True messages and True – which reduces to
→error “head of nil” the error message “head of nil”

The function isnil works in a similar way


The function isnil works in a similar way
© 2020 Christopher D. Clack 17
It is instructive to look at the types of
FUNCTIONAL PROGRAMMING these functions
For cons, if we say that a has type * and
PROGRAMMING EXAMPLES b has type ** then looking at the
function body it is clear that f takes a *
and a ** and a bool but the type of its
result is not clear, so we can call that
*** (which is also what cons returns,
REPRESENTING LISTS AS FUNCTIONS because it returns whatever is returned
by f)
The function nil takes a single argument
Types that is a function – by looking at the
function body we see it takes two error
messages and a bool. Assume the types
cons :: * -> ** -> (*->**->bool->***) -> *** of the error messages are * and ** (we
set them to be different because the
cons a b f = f a b False error value is a value of any type, and
there is nothing to force f’s first two
arguments to be the same type) and
there is nothing to constrain the return
nil :: (* -> ** -> bool -> ***) -> *** type then f must have type (* -> ** ->
bool -> ***) and nil must return type
nil f = f (error “head of nil”) ***
(error “tail of nil”) True The function head takes a single
argument that is a function – from the
function body we see that function x
takes a single argument h that itself is a
head :: ((*->**->***->*)->****)->**** function of three arguments of which it
returns the first. Thus h must have type
head x =xh * -> ** -> *** -> *. We don’t know what
where x returns so say it returns ****. Head
returns whatever x returns, so its type is
habc=a ((*->**->***->*)->****)->****
More specifically we can specify: From the rest of the code, we know a bit
more so we can constrain it to:
head :: ((*->**->bool->*)->***)->*** ((*->**->bool->*)->****)->****
© 2020 Christopher D. Clack 18
In summary, this lecture has provided
FUNCTIONAL PROGRAMMING three programming examples that have
used algebraic types and higher-order
PROGRAMMING EXAMPLES functions:
• A simple evaluator for an arithmetic
expression,
• The peg-board game of solitaire, and
• Representing lists as functions
SUMMARY

• Simple evaluator for arithmetic expression

• The peg-board game of solitaire

• Representing lists as functions

© 2020 Christopher D. Clack 19

You might also like