COMP0020-2020-lecture17-ProgrammingExamplesContinued With Captions

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 22

Functional Programming

Christopher D. Clack
2020

© 2020 Christopher D. Clack 1


FUNCTIONAL PROGRAMMING

Lecture 17
PROGRAMMING EXAMPLES
continued

© 2020 Christopher D. Clack 2


This lecture provides two worked
FUNCTIONAL PROGRAMMING examples using Miranda
1. Generating all the permutations of a
PROGRAMMING EXAMPLES list of items
2. State and parsing

CONTENTS

• Worked example 1: permutations

• Worked example 2: state and parsing

© 2020 Christopher D. Clack 3


Aim
FUNCTIONAL PROGRAMMING To define the function perms that takes
a list of items and returns a list
PROGRAMMING EXAMPLES containing all the different permutations
of the input list
For example, (perms [1,2,3]) should
return [ [1,2,3], [1,3,2], [2,3,1], [2,1,3],
[3,1,2], [3,2,1] ]
EXAMPLE 1: PERMUTATIONS: PROBLEM SPECIFICATION
We will assume the order of the items in
• Define the function perms the list is not important. So this means
perms [1,2,3] could equally return:
• Takes a list of items [ [3,2,1], [3,1,2], [1,2,3], [2,1,3], [1,3,2],
• Returns a list containing all possible [2,3,1] ]
permutations of the input list
Type
• E.g. perms [1,2,3] The function will take a list of anything
and will return a list of lists of the same
→ [ [1,2,3], [1,3,2], [2,3,1], [2,1,3], [3,1,2], [3,2,1] ] type.
So we first write the type:
• Or perms [1,2,3]
→ [ [3,2,1], [3,1,2], [2,1,3], [2,3,1], [1,3,2], [1,2,3]] perms :: [*] -> [[*]]

• Type:

perms :: [*] -> [[*]]

© 2020 Christopher D. Clack 4


Design
FUNCTIONAL PROGRAMMING We will define perms inductively:

PROGRAMMING EXAMPLES Trivially, the parameter of recursion is


the input list
Trivially, there is only one permutation
of the empty list, so the base case is
perms [] = [[]]
PERMUTATIONS DESIGN The general case for a non-empty input
list assumes the induction hypothesis
that perms of ANY shorter list will
• Use structural induction correctly return a list of all the
permutations
• Trivially, the parameter of recursion is the empty list and
For example, to calculate the perms of a
the base case is: perms [] = [[]] list [x,y,z] we assume that the recursive
calls (perms [x,y]), (perms [y,z]) and
• The induction hypothesis is that for any input list of (perms [x,z]) will all correctly return the
required permutations.
length >=1, perms will work correctly on that list with
Finding the inductive step is more
one element (any element) removed difficult. First consider a specific
example: perms [a,b,c] – the solution
• Consider the case perms [a,b,c] consists of (i) all permutations with a
occurring first, then (ii) all permutations
• the solution consists of (i) all permutations with with b occurring first, then (iii) all
permutations with c occurring first.
a occurring first, then (ii) all permutations with b Here (i) can be obtained consing a onto
occurring first, then (iii) all permutations with c the front of every item in the recursive
call perms [b,c], and therefore:
occurring first
• (i) is map ((:) a) (perms [b,c]) perms [a,b,c]
= (map ((:) a) (perms [b,c])) ++
(map ((:) b) (perms [a,c])) ++
perms [a,b,c] = (map ((:) a) (perms [b,c])) ++ (map ((:) c) (perms [a,b]))
(map ((:) b) (perms [a,c])) ++
(map ((:) c) (perms [a,b])) © 2020 Christopher D. Clack 5
However, we need to find to a definition
FUNCTIONAL PROGRAMMING that will work for an input list with any
number of elements
PROGRAMMING EXAMPLES Consider a slightly bigger input list
[a,b,c,d,e,f] - the solution here would
be
perms [a,b,c,d,e,f]
= (map ((:)a) (perms [b,c,d,e,f])) ++
PERMUTATIONS DESIGN (map ((:)b) (perms [a,c,d,e,f])) ++
(map ((:)c) (perms [a,b,d,e,f])) ++
(map ((:)d) (perms [a,b,c,e,f])) ++
A slightly bigger case: (map ((:)e) (perms [a,b,c,d,f])) ++
(map ((:)f) (perms [a,b,c,d,e]))
perms [a,b,c,d,e,f] We need to generalize this, so first
consider how to generate the arguments
= (map ((:)a) (perms [b,c,d,e,f])) ++ for the recursive calls. We can use the
(map ((:)b) (perms [a,c,d,e,f])) ++ built-in list subtraction operator “- -” to
do this (where a - - b returns a after
(map ((:)c) (perms [a,b,d,e,f])) ++ removing from a the first occurrence in
(map ((:)d) (perms [a,b,c,e,f])) ++ a of each element of b). Thus the
recursive calls become:
(map ((:)e) (perms [a,b,c,d,f])) ++
perms ([a,b,c,d,e,f] -- [x])
(map ((:)f) (perms [a,b,c,d,e])) where x successively is a, b, c, d, e or f

Consider how to generate the arguments for the recursive


calls. In each case we have:

perms ([a,b,c,d,e,f] -- [x])

where x successively is a, b, c, d, e or f

© 2020 Christopher D. Clack 6


All of the expressions using these
FUNCTIONAL PROGRAMMING recursive calls can be generated like this:
map (g [a,b,c,d,e,f]) [a,b,c,d,e,f]
PROGRAMMING EXAMPLES where
g x y = map ((:) y) (perms (x -- [y]))
Notice the style where the same value is
used in two ways. This happens twice:
• In map (g [a,b,c,d,e,f]) [a,b,c,d,e,f] the
PERMUTATIONS DESIGN list [a,b,c,d,e,f] is used both as the list
over which a function is mapped and
All of the expressions using these recursive calls can be as the argument in the partial
application (g [a,b,c,d,e,f])
generated like this: • In g x y = map ((:) y) (perms (x -- [y])
the name y is used both in the
map (g [a,b,c,d,e,f]) [a,b,c,d,e,f] expression (x - - [y]) to create the
argument for the recursive call and in
where the partial application ((:) y)
g x y = map ((:) y) (perms (x -- [y])) All that remains is to concatenate all of
these expressions together, not
forgetting the type and base case:
All that remains is to concatenate all of these expressions
perms :: [*] -> [[*]]
together, not forgetting to include the type and base case: perms [] = [[]]
perms z
= foldr (++) [] (map (g z) z)
perms :: [*] -> [[*]] where
perms [] = [[]] g x y = map ((:) y) (perms (x - - [y]))
perms z = foldr (++) [] (map (g z) z) If you prefer, the library function concat
does the same as foldr (++) []:
where
perms :: [*] -> [[*]]
g x y = map ((:) y) (perms (x - - [y])) perms [] = [[]]
perms z
= concat (map (g z) z)
where
g x y = map ((:) y) (perms (x - - [y]))
© 2020 Christopher D. Clack 7
State
FUNCTIONAL PROGRAMMING Consider a program that takes a list of
strings (each represents a word) and
PROGRAMMING EXAMPLES generates an output that depends on
what incoming words are detected and
in what order.
This is an example of a process called
“parsing”.
EXAMPLE 2: STATE The program is typically designed in a
modular way, to make it easier to
A program takes a list of strings and generates an output maintain and upgrade later – several
functions are used, each of which takes
that depends on the order of words in the input as input and returns as output a two-
tuple comprising (i) a list of strings that
has not yet been processed, and (ii) the
This is an example of a process called “parsing” state of the output so far.
The output of the program is the second
Modular design makes it easier to maintain and upgrade value of the output tuple of the last of
these functions. The functions f1, f2, …
the code. Several functions are used, each taking as input and states S1, S2, … and remaining
and returning as output as output a two-tuple comprising inputs R1, R2, … are chained together as
illustrated.
(i) a list of strings that has not yet been processed, and (ii)
the state of the output so far.

The functions f1, f2, … and states S1, S2, … and remaining
inputs R1, R2, … are chained together as illustrated:

f1 f2 f3

(R1,S1) (R2,S2) (R3,S3) (R4,S4)


© 2020 Christopher D. Clack 8
Here is some example code. The
FUNCTIONAL PROGRAMMING example illustrates how state is passed
from one function to the next, but it
PROGRAMMING EXAMPLES does not give the definitions of the
functions, nor does it define the start
state

EXAMPLE 2: STATE

Example code:

prog xs = s4
where
(r4, s4) = f3 (r3, s3)
(r3, s3) = f2 (r2, s2)
(r2, s2) = f1 (r1, s1)
r1 = xs
s1 = …. (the start state, whatever it is)

f1 f2 f3

(R1,S1) (R2,S2) (R3,S3)


© 2020 Christopher D. Clack 9
The previous slide was an example of
FUNCTIONAL PROGRAMMING “sequential plumbing” of state.
This can also be written more succinctly
PROGRAMMING EXAMPLES using foldr, as illustrated.
Notice how foldr is applied to a list of
functions f1, f2, f3 that are in reverse
order, so that f1 is the function applied
to the initial state (xs, s1). This is
EXAMPLE 2: STATE illustrated in the diagram below.

Example “sequential plumbing” using foldr:

prog xs = snd (foldr g (xs, s1) [f3, f2, f1])


where
g f (r,s) = f (r,s)
s1 = …. (the start state, whatever it is)

f3 f2 f1

(R4,S4) (R3,S3) (R2,S2) (R1,S1)


© 2020 Christopher D. Clack 10
More complex plumbing of state can
FUNCTIONAL PROGRAMMING occur with branching recursion
We won’t look at code for this – just a
PROGRAMMING EXAMPLES diagram illustrating how the state could
be passed up and down the tree of
recursive calls
The disadvantage of this plumbing is
that it must be done explicitly (rather
EXAMPLE 2: STATE than by access to a shared global
variable, as with non-declarative
(R1,S1) (R14,S14) languages)
The advantage of this plumbing is that it
f1 is done explicitly, so that the ordering of
every change in state is clear (and there
can be no surprises, as might occur in
non-declarative languages where code
(R2,S2) (R7,S7) (R13,S13) (R8,S8) might be modified so that it changes the
way a global variable is updated without
thought to how this might interfere with
the use that other functions are making
f2 f3 of that global variable)
Fortunately, functional programming
also provides ways to hide some of the
complexity of both sequential and non-
(R3,S3) (R5,S5) (R9,S9) (R11,S11) sequential state update – later in this
(R4,S4) (R6,S6) (R10,S10) (R12,S12) lecture we will discuss “parser
combinators” as an example.

f4 f5 f6 f7

© 2020 Christopher D. Clack 11


This illustration provides a concrete
FUNCTIONAL PROGRAMMING example of sequential state update, with
definitions for state and for functions f1,
PROGRAMMING EXAMPLES f2 and f3.
Notice how foldr is used to hide the
sequential plumbing of state (where (xs,
s1) is the initial state).
The functions f1, f2 and f3 are defined
EXAMPLE 2: STATE as partial applications of a “generator”
function fn. Each function either finds
the word or phrase it is looking for at
state ::= Empty | Tokens [char] state the head of the incoming list, or it
ignores the head of the list.
prog :: [[char]] -> state This is a very simple example and the
result isn’t entirely satisfactory for
prog xs = snd (foldr g (xs, s1) [f3, f2, f1]) parsing, since the output is in reverse
where order.
g f (r,s) = f (r,s)
s1 = Empty
fn match ([],s) = ([],s)
fn match ((match:rest),s) = (rest, Tokens match s)
fn match ((any:rest), s) = (rest, s)
f1 = fn “Hello”
f2 = fn “How are you?”
f3 = fn “What’s your name?”

© 2020 Christopher D. Clack 12


For completeness, here’s a version of
FUNCTIONAL PROGRAMMING the code on the previous slide with the
output in the same order as the input.
PROGRAMMING EXAMPLES The unchanged code is in grey.

EXAMPLE 2: STATE

state ::= Empty | Tokens [char] state

prog :: [[char]] -> state


prog xs = snd (foldr g (xs, s1) [f3, f2, f1])
where
g f (r,s) = f (r,s)
s1 = Empty
fn match ([],s) = ([],s)
fn match ((match:rest),s) = (rest, putlast match s)
fn match ((any:rest), s) = (rest, s)
f1 = fn “Hello”
f2 = fn “How are you?”
f3 = fn “What’s your name?”

putlast :: [char] -> state -> state


putlast x Empty = Tokens x Empty
putlast x (Tokens y z) = Tokens y (putlast x z)
© 2020 Christopher D. Clack 13
Converting a sequential input of
FUNCTIONAL PROGRAMMING characters into a structured format is a
common task. For example:
PROGRAMMING EXAMPLES • creating a structured form of text
read from a file, or
• converting user input in a structured
form of commands and arguments, or
• converting source code into a parse
MORE ON PARSING tree for use in a compiler

• The overall process of converting a string into an This activity is often broken down into
two steps:
internal format is often broken down into two steps: • “lexing” (or “lexical analysis”) uses a
• lexer: [char] -> [token] function called a “lexer” to convert a
list of characters into a list of symbols
• often simple and may not need to be broken (called “tokens” or “lexemes”)
down into subsidiary functions representing different kinds of items
in the input – verbs, nouns,
• parser: [token] -> structure (e.g. a tree output) punctuation, numbers, etc
• more complex and can use the state-passing • “parsing” (or “syntactic analysis”)
uses a function called a “parser” to
structure previously described convert a list of symbols (tokens, e.g.
constructors of an algebraic type) into
• Simplistic example: a structured form, sometimes called a
"parse tree"
lexer “Bob paid Jane 23.50”
• As a simplistic example:
=> [Name “Bob”, Verb “paid”, Name “Jane”, Number 23.50] lexer "Bob paid Jane 23.50”
might evaluate to
[Name "Bob", Verb "paid", Name "Jane",
Number 23.50]

© 2020 Christopher D. Clack 14


This slide illustrates how simple lexer
FUNCTIONAL PROGRAMMING code can be.
Function lexer has type [char] -> [token]
PROGRAMMING EXAMPLES where token is an algebraic type defined
at the top.
token captures information about
incoming collections of characters –
whether they represent names, verbs,
A SIMPLE LEXER numbers, conjunctions, prepositions etc.
This is done to help the parser.
token ::= Name [char] | Verb [char] | Number num |
Conjunction [char] |Preposition [char] | The lexer code first uses the subfunction
words to use spaces to split the input
Determiner [char] into words (it produces a [[char]]) and
lexer :: [char] -> [token] then each word is processed separately
lexer xs by mapping function word_to_token
= map word_to_token (words xs) over the output of words.
where The subfunction word_to_token takes a
word_to_token x single word (of type [char]) and tests it
to determine which token should be
= Number (numval x), if (isnum x) output.
= Determiner x, if (member ["a","an","the"] x)
= Verb x, if (member ["paid","delivered"] x) Aside: Notice how isnum uses higher-
order functions foldr and filter, and how
= Conjunction x, if (member ["and","or"] x) the function used in foldr is a function
= Preposition x, if (member ["to","from"] x) composition of two functions:
= Name x, otherwise ((&).(member “1234567890.”))
isnum x = (foldr ((&).(member "1234567890.")) True x)
At each recursive call, foldr applies this
& (#(filter (=‘.’) x) <= 1) function composition to an element of
words [] = [] the list x and the Boolean value
words (a:x) = []:(words x), if a=‘ ‘ calculated so far. For example:
= (a:x1):xrest, otherwise ((&).(member “1234567890.”)) ‘3’ True
where => ((&) (member “1234567890.” ‘3’)) True
(x1:xrest) = words x, if x~=[] => ((&) True True)
=> True
= []:[], otherwise

© 2020 Christopher D. Clack 15


The top-level parser is looking for a
FUNCTIONAL PROGRAMMING successful parse that fits a set of
syntactic rules and otherwise returns a
PROGRAMMING EXAMPLES null result.
Imagine a parser of arithmetic
expressions – the relevant types might
be:
parser :: [token] -> tree
A SIMPLE PARSER token ::= TNumber num | TOp op | TLbracket |
TRbracket

• The top-level parser is looking for a successful parse and tree ::= NullParse | Number num |
BinaryOp tree op tree |
otherwise returns a null result. Brackets tree
op ::= Plus | Minus | Times | Divide
Sometimes there might be more than
• Imagine a parser of arithmetic expressions, using these types: one successful parse for a given input.
parser :: [token] -> tree For example the input
[TNumber 3, TOp Plus, TNumber 4, TOp Plus,
token ::= TNumber num | TOp op | TLbracket | TRbracket TNumber 5]
can parse correctly in two ways:
tree ::= NullParse | Number num | BinaryOp tree op tree |
Brackets tree • BinaryOp (BinaryOp (Number 3) Plus
(Number 4)) Plus (Number 5)
op ::= Plus | Minus | Times | Divide
• BinaryOp (Number 3) Plus (BinaryOp
(Number 4) Plus (Number 5))
• There might be more than one successful parse. E.g. We therefore generalise the type of
[TNumber 3, TOp Plus, TNumber 4, TOp Plus, TNumber 5] parser to produce a list of correct
can parse in two ways: results. The new type will be [token] ->
[tree]. A null parse can now also be
BinaryOp (BinaryOp (Number 3) Plus (Number 4)) Plus (Number 5) represented by an empty output list
BinaryOp (Number 3) Plus (BinaryOp (Number 4) Plus (Number 5)) Subfunction types for use in foldr would
typically have type:
• So output a list of correct results, so parser:: [token] -> [tree]. [([token],tree)] -> [([token],tree)]
However, the following slides will focus
• Subfunction types for use in foldr would typically have type: on simpler subfunctions of type
[([token],tree)] -> [([token],tree)] ([token],tree) -> [([token],tree)]
© 2020 Christopher D. Clack 16
We want to create generic functions,
FUNCTIONAL PROGRAMMING and both the type of the tokens and the
way that the structure is described will
PROGRAMMING EXAMPLES vary between applications. Thus for now
we’ll use the polymorphic type * for the
token and ** for the result (which may
or may not be a tree). A top-level parser
therefore can have type [*] -> [**]
DESIGN Subsidiary parser functions typically
return a list of 2-tuples each containing
(i) the part of the input string they have
• Further generalise the type of a parser. Instead of not yet processed together with (ii) the
resultant structure of what was parsed.
[token] -> [tree] use polymorphic types: [*] -> [**] For convenience we’ll also say that they
take one such tuple, so they must have
type
• A complex parser uses subsidiary parser functions each ([*],**) -> [([*] ,**)]
of which parses only a part of the input and has type: We can give this generic type a name:
([*],**) -> [([*], **)] t_parser * ** == ([*],**) -> [ ([*], **) ]
The name t_parser has been chosen to
• Give this type a name: distinguish the type name t_parser from
the function name parser.
For example, using the algebraic types
t_parser * ** == ([*],**) -> [ ([*], **) ] token and tree defined on the previous
page, a subfunction f1 can have type:
• For any given parser we can instantiate the * and ** to f1 :: t_parser token tree
be what we want. For example, using the algebraic
types token and tree defined on the previous page, a
subfunction f1 can have type:

f1 :: t_parser token tree


© 2020 Christopher D. Clack 17
Here are some simple higher-order
FUNCTIONAL PROGRAMMING generator functions that produce parser
subfunctions of type t_parser * **
PROGRAMMING EXAMPLES The generator function symbol can be
partially applied to its first argument
that is a symbol – the result is a parser
subfunction for that symbol. For
example we can define a new parser
DESIGN subfunction p1 as follows:
p1 :: t_parser char [char]
p1 = symbol ‘A’
Two simple generator functions to create parser And now p1 (“ABC”,[]) => [("BC",”A”)]
subfunctions: Note that * must be a type that can be
tested for equality. Also, a null parse is
symbol :: * -> t_parser * [*] represented by the empty list.
symbol a ([],s) = [] The generator function satisfy is more
general – it takes a predicate function
symbol a ((x:xs),s) = [ (xs, s++[a]) | a = x ] for its first argument and a partial
application would return a parser
• p1 :: ([char],[char]) -> [([char], [char])] subfunction for any symbol that satisfies
the predicate. For example:
p1 = symbol ‘A’
p2 :: t_parser char [char]
• p1 (“ABC”,[]) => [(“BC”, “A”)] p2 = satisfy (=‘A’)
And now p2 ("ABC”,[]) => [("BC",”A”)]
satisfy :: (* -> bool) -> t_parser * [*] Note that these subfunctions haven’t
done much – the "structured format" in
satisfy p ([],s) = [] each case is not very structured, it's just
satisfy p ((x:xs),s) = [ (xs, s++[x]) | p x ] a [char]. Of course you can create your
own parser generator functions where
• p2 :: ([char],[char]) -> [([char], [char])] the output is more complex.

p2 = satisfy (=‘A’)
• p2 (“ABC”,’[]) => [(“BC”, “A”)]
© 2020 Christopher D. Clack 18
How do we construct complex parsers
FUNCTIONAL PROGRAMMING from smaller, simpler, parsers?
A syntax specification for the input
PROGRAMMING EXAMPLES might say that the input might be (for
example) Token1 followed by Token2
followed by either Token3 or Token4
We now define two parser combinators
that can be used to combine individual
PARSER COMBINATORS parser subfunctions for each of the
tokens into a complex parser
subfunction for the sequence of tokens
then :: t_parser * ** -> t_parser * ** -> t_parser * ** The function then is a parser combinator
then p1 p2 (xs,s) for sequential composition. First p1 is
= [ (xs2, s2) | (xs1, s1) <- p1 (xs,s); applied to the input (xs,s) and generates
a list of results. A first loop binds (xs1,
(xs2, s2) <- p2 (xs1,s1)] s1) to each of the results in turn, and for
each such binding p2 is applied to
(xs1,s1) to generate a list of results; a
second loop binds (xs2, s2) to each of
the results in turn, and each time a new
value (xs2, s2) is created for the final list
alt :: t_parser * ** -> t_parser * ** -> t_parser * ** of results. The result of the function is
alt p1 p2 (xs,s) the list of all possible tuples comprising
the remaining (unprocessed) input xs2
= (p1 (xs,s)) ++ (p2 (xs,s)) with the associate parsed output s2.
The function alt is simpler; it represents
choice and merely needs to concatenate
the results of the two parse functions p1
and p2.
then and alt show two different kinds of
“plumbing” of state. You can create
additional combinators for different
kinds of plumbing.

© 2020 Christopher D. Clack 19


As a simple example of constructing a
FUNCTIONAL PROGRAMMING larger parser subfunction using the
parser combinators, consider a
PROGRAMMING EXAMPLES subfunction parseKWdef to parse a
keyword with two alternatives: “def” or
“Def”. Here the input is just a [char].

parseKWdef :: parser char [char]


PARSER COMBINATORS parseKWdef
= ((symbol ‘d’) $alt (symbol ‘D’))
parseKWdef :: t_parser char [char] $then
parseKWdef (symbol ‘e’)
= ((symbol ‘d’) $alt (symbol ‘D’)) $then
(symbol ‘f’)
$then
(symbol ‘e’) Example usage is illustrated: parseKWdef
reports correct parsing for both “def”
$then and “Def” but not for “deaf”.

(symbol ‘f’) Notice how this makes use of Miranda’s


syntax for infix functions – we prefix the
names of the parser combinators then
• Usage and alt with the dollar character so they
can be used in infix position (the
opposite of wrapping an operator in
Miranda parseKWdef ("def",[]) brackets to use it in prefix position).
[("","def")] Thus the two expressions (then p1 p2)
and (p1 $then p2) give identical results
Miranda parseKWdef ("Def",[]) These parser combinators provide an
[("","Def")] elegant way to construct complex parser
subfunction code from simpler parser
subfunctions.
Miranda parseKWdef ("deaf",[])
[] © 2020 Christopher D. Clack 20
It is possible to further generalize parser
FUNCTIONAL PROGRAMMING combinators by using a “parser
combinator generator”.
PROGRAMMING EXAMPLES This example illustrates a parser
combinator generator gthen that, when
partially applied to its first argument,
provides a version of the parser
combinator called then. Different
PARSER COMBINATORS versions are possible, depending on the
value of the first argument.
The function body is a function
A parser combinator generator: composition, waiting to be applied to an
argument of type ([*],*). The first
function to be applied to the input is the
gthen :: (**->**->**) -> composition ((map g).p1), which first
t_parser * ** -> t_parser * ** -> t_parser * ** applies the parser p1 and then maps the
function g across all of the results from
gthen f p1 p2 p1. In each case, the function g applies
= (concat.((map g).p1)) p2 to the result from p1, calculates the
result unparsed value xs2, and uses f to
where combine the two results s1 and s2. The
multiple lists are then concatenated.
g (xs1,s1) = [(xs2, f s1 s2) |
For example, the previously defined
(xs2, s2) <- (p2 (xs1,s1))] parser combinator then could
alternatively have been derived using
the combinator generator gthen as
• Example: illustrated below.
then :: t_parser * ** -> t_parser * ** -> t_parser * **
then = gthen (swap const)
where
swap f x y = f y x

© 2020 Christopher D. Clack 21


In summary, this lecture has provided
FUNCTIONAL PROGRAMMING two worked examples using Miranda
1. Generating all the permutations of a
PROGRAMMING EXAMPLES list of items
2. State and parsing
The second example ended with a
“parser combinator” programming style
that is inspired by the combinatorial
SUMMARY logic of Schönfinkel, Curry and Feys.
This style is both very elegant and a
• Worked example 1: permutations hallmark of advanced functional
programming
It is important that you should develop
• Worked example 2: state and parsing your ability with Miranda programming,
and a wide range of exercises have been
provided to you for this purpose. These
exercises are not assessed but your
solutions and questions can be the basis
for future discussion

© 2020 Christopher D. Clack 22

You might also like