Peter Sewell - Semantics Notes
Peter Sewell - Semantics Notes
Peter Sewell
Computer Laboratory
University of Cambridge
Lecture Theatre 1
Tuesday / Thursday 12pm
9 October 18 November 2014
Time-stamp:
c
Sam
Staton 20092013
c
Peter
Sewell 20032009
Contents
Syllabus
Learning Guide
Summary of Notation
1 Introduction
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12
13
21
28
30
3 Induction
3.1 Abstract Syntax and Structural Induction
3.2 Inductive Definitions and Rule Induction .
3.3 Example proofs . . . . . . . . . . . . . . .
3.4 Exercises . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
31
33
35
38
45
4 Functions
4.1 Abstract syntax up to alpha conversion, and
4.2 Function Behaviour . . . . . . . . . . . . . .
4.3 Function Typing . . . . . . . . . . . . . . .
4.4 Local Definitions and Recursive Functions .
4.5 Implementation . . . . . . . . . . . . . . . .
4.6 L2: Collected Definition . . . . . . . . . . .
4.7 Exercises . . . . . . . . . . . . . . . . . . .
substitution
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
46
48
52
56
58
61
64
67
5 Data
5.1 Products and sums . . .
5.2 Datatypes and Records
5.3 Mutable Store . . . . . .
5.4 Evaluation Contexts . .
5.5 L3: Collected definition
5.6 Exercises . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
68
68
71
73
76
77
81
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
105
108
112
C How to do Proofs
C.1 How to go about it . . . . . . .
C.2 And in More Detail... . . . . .
C.2.1 Meet the Connectives .
C.2.2 Equivalences . . . . . .
C.2.3 How to Prove a Formula
C.2.4 How to Use a Formula .
C.3 An Example . . . . . . . . . . .
C.3.1 Proving the PL . . . . .
C.3.2 Using the PL . . . . . .
C.4 Sequent Calculus Rules . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
118
118
120
120
120
121
123
124
124
125
125
Syllabus
This course is a prerequisite for Types (Part II), Denotational Semantics (Part II), and
Topics in Concurrency (Part II).
Aims
The aim of this course is to introduce the structural, operational approach to programming language semantics. It will show how to specify the meaning of typical programming
language constructs, in the context of language design, and how to reason formally about
semantic properties of programs.
Lectures
Introduction. Transition systems. The idea of structural operational semantics.
Transition semantics of a simple imperative language. Language design options.
Types. Introduction to formal type systems. Typing for the simple imperative language. Statements of desirable properties.
Induction. Review of mathematical induction. Abstract syntax trees and structural induction. Rule-based inductive definitions and proofs. Proofs of type safety
properties.
Functions. Call-by-name and call-by-value function application, semantics and typing. Local recursive definitions.
Data. Semantics and typing for products, sums, records, references.
Subtyping. Record subtyping and simple object encoding.
Semantic equivalence. Semantic equivalence of phrases in a simple imperative language, including the congruence property. Examples of equivalence and non-equivalence.
Concurrency. Shared variable interleaving. Semantics for simple mutexes; a serializability property.
Objectives
At the end of the course students should
be familiar with rule-based presentations of the operational semantics and type systems
for some simple imperative, functional and interactive program constructs
be able to prove properties of an operational semantics using various forms of induction
(mathematical, structural, and rule-based)
be familiar with some operationally-based notions of semantic equivalence of program
phrases and their basic properties
Recommended reading
Hennessy, M. (1990). The semantics of programming languages. Wiley.
Out of print, but available on the web at
http://www.scss.tcd.ie/Matthew.Hennessy/slexternal/reading.php.
* Pierce, B.C. (2002). Types and programming languages. MIT Press. ebook available at
http://search.lib.cam.ac.uk/?itemid=|eresources|4228596.
Winskel, G. (1993). The formal semantics of programming languages. MIT Press.
Learning Guide
The books are all available in the Computer Laboratory Library. Books:
Hennessy, M. (1990). The Semantics of Programming Languages. Wiley. Out of print.
Introduces many of the key topics of the course. Theres a copy on the web at
http://www.scss.tcd.ie/Matthew.Hennessy/slexternal/reading.php.
Two essays in: Wand, I. and R. Milner (Eds) (1996), Computing Tomorrow, CUP:
Hoare, C. A. R.. Algebra and Models.
Milner, R. Semantic Ideas in Computing.
Two accessible essays giving somewhat different perspectives on the semantics of computation
and programming languages.
Andrew Pitts lectured this course until 2002. The syllabus has changed, but you might
enjoy his notes, still available at http://www.cl.cam.ac.uk/teaching/2001/Semantics/.
Pierce, B. C. (ed) (2005) Advanced Topics in Types and Programming Languages. MIT
Press.
This is a collection of articles by experts on a range of programming-language semantics topics.
Most of the details are beyond the scope of this course, but it gives a good overview of the
state of the art. The contents are listed at http://www.cis.upenn.edu/~bcpierce/attapl/.
3. 7.2: 37, 7.1: (39), 40; 6.1: 31, 32, 35; 2003.6.12, mock tripos from www.
Tripos questions: This version of the course was first given in 20022003. The questions
since then are directly relevant, and there is an additional mock question on the course web
page. The previous version of the course (by Andrew Pitts) used a slightly different form
of operational semantics, big-step instead of small-step (see Page 63 of these notes), and
different example languages, so the notation in most earlier questions may seem unfamiliar
at first sight.
These questions use only small-step and should be accessible: 1998 Paper 6 Question 12,
1997 Paper 5 Question 12, and 1996 Paper 5 Question 12.
These questions use big-step, but apart from that should be ok: 2002 Paper 5 Question 9,
2002 Paper 6 Question 9, 2001 Paper 5 Question 9, 2000 Paper 5 Question 9, 1999 Paper 6
Question 9 (first two parts only), 1999 Paper 5 Question 9, 1998 Paper 5 Question 12, 1995
Paper 6 Question 12, 1994 Paper 7 Question 13, 1993 Paper 7 Question 10.
These questions depend on material which is no longer in this course (complete partial
orders, continuations, or bisimulation see the Part II Denotational Semantics and Topics
in Concurrency courses): 2001 Paper 6 Question 9, 2000 Paper 6 Question 9, 1997 Paper 6
Question 12, 1996 Paper 6 Question 12, 1995 Paper 5 Question 12, 1994 Paper 8 Question
12, 1994 Paper 9 Question 12, 1993 Paper 8 Question 10, 1993 Paper 9 Question 10.
Feedback: Please do complete the on-line feedback form at the end of the course, and let
me know during it if you discover errors in the notes or if the pace is too fast or slow. A list
of corrections will be on the course web page.
Acknowledgements (P. Sewell): These notes are a modification of the notes that Sam
Staton used for the course, 20102013.
Acknowledgements (S. Staton): These notes are a modification of the notes that Peter
Sewell used for the course, 20032009.
Acknowledgements (P. Sewell): These notes draw, with thanks, on earlier courses by
Andrew Pitts, on Benjamin Pierces book, and many other sources. Any errors are, of
course, newly introduced by me.
Summary of Notation
Each section is roughly in the order that notation is introduced. The grammars of the
languages are not included here, but are in the Collected Definitions of L1, L2 and L3 later
in this document.
x .(x )
x .(x )
a A
{a1 , ..., an }
A1 A2
A1 A2
A1 A2
A1 A2
and
or
implies
not
for all
exists
element of
the set with elements a1 , ..., an
union
intersection
subset or equal
cartesian product (set of pairs)
e e
functions
reduction (or transition) step
reflexive transitive closure of
the k -fold composition of
has an infinite reduction sequence (a unary predicate)
cannot reduce (a unary predicate)
in type environment , expression e has type T
e is a value
the set of free variables of e
the expression resulting from substituting e for x in e
the expression resulting from applying the substituting to e
big-step evaluation
store s is well-typed with respect to type environment
type T is a subtype of type T
semantic equivalence (informal)
semantic equivalence at type T with respect to type
environment
single thread transition step, labelled with action a
Particular sets
B = {true, false}
L = {l , l1 , l2 , ...}
Z = {.., 1, 0, 1, ...}
N = {0, 1, ...}
X = {x, y, ...}
LAB = {p, q, ...}
M = {m, m0 , m1 , ...}
T
Tloc
L1
TypeEnv
TypeEnv2
Metavariables
b B
n Z
L
op
e, f
v
s
T T
Tloc Tloc
i, k, y
c
c
R
(H , c)
SR
x X
lab LAB
E
C
m M
M
a
Other
C [e]
hole in a context
context C with e replacing the hole
Introduction
Semantics of Programming Languages
Peter Sewell
Slide 1
1B, 12 lectures
201415
In this course we will take a close look at programming languages. We will focus on how to
define precisely what a programming language is i.e., how the programs of the language
behave, or, more generally, what their meaning, or semantics, is.
Semantics What is it?
How to describe a programming language? Need to give:
Slide 2
Slide 3
Many programming languages that you meet are described only in natural language, e.g.
the English standards documents for C, Java, XML, etc. These are reasonably accessible
(though often written in standardsese), but there are some major problems. It is very
hard, if not impossible, to write really precise definitions in informal prose. The standards
often end up being ambiguous or incomplete, or just too large and hard to understand.
That leads to differing implementations and flaky systems, as the language implementors
and users do not have a common understanding of what it is. More fundamentally, natural
language standards obscure the real structure of languages its all too easy to add a feature
and a quick paragraph of text without thinking about how it interacts with the rest of the
language.
Instead, as we shall see in this course, one can develop mathematical definitions of how
programs behave, using logic and set theory (e.g. the definition of Standard ML, the .NET
CLR, recent work on XQuery, etc.). These require a little more background to understand
and use, but for many purposes they are a much better tool than informal standards.
Slide 4
Slide 5
Slide 6
class M {
public static void Main() {
IntThunk[] funcs = new IntThunk[11];
for (int i = 0; i <= 10; i++)
{
funcs[i] = delegate() { return i; };
}
foreach (IntThunk f in funcs)
{
System.Console.WriteLine(f());
}
}
}
JavaScript
Slide 7
Slide 8
Slide 9
Operational semantics
Denotational semantics
Axiomatic, or Logical, semantics
Operational: define the meaning of a program in terms of the computation steps it takes in
an idealized execution. Some definitions use structural operational semantics, in which the
intermediate states are described using the language itself; others use abstract machines,
which use more ad-hoc mathematical constructions.
Denotational: define the meaning of a program as elements of some abstract mathematical
structure, e.g. regarding programming-language functions as certain mathematical functions.
cf. the Denotational Semantics course.
Axiomatic or Logical: define the meaning of a program indirectly, by giving the axioms of
a logic of program properties. cf. Specification and Verification.
Toy languages
Real programming languages are large, with many features and, often,
with redundant constructs things that can be expressed in the rest of the
Slide 10
language.
When trying to understand some particular combination of features its
usual to define a small toy language with just what youre interested in,
then scale up later. Even small languages can involve delicate design
choices.
Whats this course?
Core
10
Operational semantics
L11,2,3,4
Type systems
Implementations
5,6
Language design choices
(functions and recursive definitions) L2
Inductive definitions
Subtyping
Semantic
10
9
Equivalence
and Objects
Concurrency12
(assignment and while )
Slide 12
In the core we will develop enough techniques to deal with the semantics of a non-trivial
small language, showing some language-design pitfalls and alternatives along the way. It
will end up with the semantics of a decent fragment of ML. The second part will cover a
selection of more advanced topics.
The Big Picture
Java and
Logic
ML
RLFA
C&DS
& Proof
ss
ss
s
s
ss
ss
Computability
ss
ss
sss
% yssst
Concepts in
Discrete
Maths
Slide 13
Compiler
Construction
and Optimising
Compilers
Semantics
rrr
Programming
Languages
Advanced
Programming
Languages?
rrr
rr
r
r
rrr
rrr
r
rrr
rrr
r
r
y r
Topics in
Types
Concurrency
Spec&Ver I,II
Denotational
Semantics
Admin
Slide 14
Not all previous Tripos questions are relevant (see the notes)
Exercises in the notes.
Implementations on web.
Books (Hennessy, Pierce, Winskel)
11
L1
Slide 15
L1 Example
L1 is an imperative language with store locations (holding integers),
conditionals, and while loops. For example, consider the program
l2 := 0;
Slide 16
while !l1
1 do (
l2 :=!l2 +!l1 ;
l1 :=!l1 + 1)
in the initial store {l1
7 3, l2 7 0}.
L1 Syntax
B = {true, false}
Z = {..., 1, 0, 1, ...}
Locations L = {l , l0 , l1 , l2 , ...}
Booleans b
Integers n
Operations
Slide 17
op ::= + |
Expressions
| e1 ; e2 |
while
e1 do e2
Points to note:
well return later to exactly what the set L1 is when we talk about abstract syntax
unbounded integers
abstract locations cant do pointer arithmetic on them
untyped, so have nonsensical expressions like 3 + true
what kind of grammar is that (c.f. RLFA)?
dont have expression/command distinction
doesnt much matter what basic operators we have
carefully distinguish metavariables b, n, , op , e etc. from program locations l etc..
12
2.1
Operational Semantics
In order to describe the behaviour of L1 programs we will use structural operational semantics to define various forms of automata:
Transition systems
A transition system consists of
state c .
To compare with the automata you saw in Regular Languages and Finite Automata: a
transition system is like an NFA with an empty alphabet (so only transitions) except (a)
it can have infinitely many states, and (b) we dont specify a start state or accepting states.
Sometimes one adds labels (e.g. to represent IO) but mostly well just look at the values of
terminated states, those that cannot do any transitions.
Notation.
is the reflexive transitive closure of , so c c iff there exist k 0 and
c0 , .., ck such that c = c0 c1 ... ck = c .
Slide 19
Take configurations to be pairs he, si of an expression e and a store s , so
our transition relation will have the form
he, si he , s i
Definition. A finite partial function f from a set A to a set B is a set containing a finite
number n 0 of pairs {(a1 , b1 ), ..., (an , bn )}, often written {a1 7 b1 , ..., an 7 bn }, for which
i {1, .., n}.ai A (the domain is a subset ofA)
i {1, .., n}.bi B (the range is a subset of B )
i {1, .., n}, j {1, .., n}.i 6= j ai 6= aj (f is functional, i.e. each element of A is
mapped to at most one element of B )
For a partial function f , we write dom(f ) for the set of elements in the domain of f (things
that f maps to something) and ran(f ) for the set of elements in the range of f (things that
something is mapped to by f ). For example, for the store s above we have dom(s) = {l1 , l3 }
and ran(s) = {7, 23}. Note that a finite partial function can be empty, just {}.
We write store for the set of all stores.
13
hl := 2+!l ,
{l 7 3}i
hl := 2 + 3, {l 7 3}i
hl := 5,
{l 7 3}i
hskip,
{l 7 5}i
Slide 20
V = B Z {skip}.
Say he, si is stuck if e is not a value and he, si
6. For example
We could define the values in a different, but equivalent, style: Say values v are expressions
from the grammar v ::= b | n | skip.
Now define the behaviour for each construct of L1 by giving some rules that (together)
define a transition relation .
L1 Semantics (2 of 4) Rules (basic operations)
(op +)
hn1 + n2 , si hn, si
if n
= n1 + n2
(op )
hn1 n2 , si hb, si
if b
= (n1 n2 )
Slide 21
(op1)
(op2)
he1 , si he1 , s i
he1 op e2 , si he1 op e2 , s i
he2 , si he2 , s i
hv op e2 , si hv op e2 , s i
How to read these? The rule (op +) says that for any instantiation of the metavariables
n, n1 and n2 (i.e. any choice of three integers), that satisfies the sidecondition, there is a
transition from the instantiated configuration on the left to the one on the right.
We use a strict naming convention for metavariables: n can only be instantiated by integers,
not by arbitrary expressions.
The rule (op1) says that for any instantiation of e1 , e1 , e2 , s, s (i.e. any three expressions
and two stores), if a transition of the form above the line can be deduced then we can
deduce the transition below the line.
Observe that as you would expect none of these first rules introduce changes in the store
part of configurations.
14
Example
If we want to find the possible sequences of transitions of
(op1)
Slide 22
h2 + 3, i h5, i
h(2 + 3) + (6 + 7), i h5 + (6 + 7), i
(op +)
(op2)
h6 + 7, i h13, i
h5 + (6 + 7), i h5 + 13, i
(op +)
h5 + 13, i h18, i
(assign1)
Slide 23
(assign2)
h := n, si hskip, s + { 7 n}i
he, si he , s i
h := e, si h := e , s i
(seq1)
(seq2)
hskip; e2 , si he2 , si
he1 , si he1 , s i
he1 ; e2 , si he1 ; e2 , s i
15
if
dom(s)
Example
hl := 3; !l , {l 7 0}i
hskip; !l , {l 7 3}i
h!l , {l 7 3}i
h3, {l 7 3}i
Slide 24
hl := 3; l :=!l , {l 7 0}i ?
h15+!l , i
(if2)
Slide 25
(if3)
he1 , si he1 , s i
hif e1 then e2 else e3 , si hif e1 then e2 else e3 , s i
(while)
Slide 26
then
he, si ?
That concludes our definition of L1. The full definition is collected on page 28.
Determinacy
Slide 27
he1 , s1 i and
he, si he2 , s2 i then he1 , s1 i = he2 , s2 i.
Proof see later
Note that top-level universal quantifiers are usually left out the theorem really says For
all e, s, e1 , s1 , e2 , s2 , if he, si he1 , s1 i and he, si he2 , s2 i then he1 , s1 i = he2 , s2 i.
16
L1 implementation
Many possible implementation strategies, including:
1. animate the rules use unification to try to match rule conclusion
left-hand-sides against a configuration; use backtracking search to find
all possible transitions. Hand-coded, or in Prolog/LambdaProlog/Twelf.
Slide 28
Slide 29
datatype expr =
Integer of int
| Boolean of bool
Slide 30
The expression and operation datatypes have essentially the same form as the abstract
grammar. Note, though, that it does not exactly match the semantics, as that allowed
arbitrary integers whereas here we use the bounded Moscow ML integers so not every
term of the abstract syntax is representable as an element of type expr, and the interpreter
will fail with an overflow exception if + overflows.
17
Store operations
Define auxiliary operations
Slide 31
lookup :
update :
which both return NONE if given a location that is not in the domain of the
store. Recall that a value of type T
NONE, if he, si 6,
(e,s), if it has a transition he, si he , s i.
or SOME
(you might think it would be better ML style to use exceptions instead of these options;
that would be fine).
(op +), (op )
...
if (is value e1) then
case reduce (e2,s) of
SOME (e2,s) =>
SOME (Op(e1,opr,e2),s)
Slide 34
18
Note that the code depends on global properties of the semantics, including the fact that it
defines a deterministic transition system, so the comments indicating that particular lines
of code implement particular semantic rules are not the whole story.
(assign1), (assign2)
Slide 35
=>
(case reduce (e,s) of
SOME (e,s) =>
SOME(Assign (l,e), s)
| NONE => NONE ) )
The many-step evaluation function
Slide 36
The full interpreter code is in Appendix A, and you can also download it from the course
website, in the file l1.ml, together with a pretty-printer and the type-checker we will come
to soon. For comparison, there is also a Java implementation in l1.java.
The Java Implementation
Quite different code structure:
Slide 37
the ML groups together all the parts of each algorithm, into the
reduce, infertype, and prettyprint functions;
the Java groups together everything to do with each clause of the
abstract syntax, in the IfThenElse, Assign, etc. classes.
19
Slide 38
(op2b)
he2 , si he2 , s i
he1 op e2 , si he1 op e2 , s i
he1 , si he1 , s i
he1 op v , si he1 op v , s i
h := n, si hskip, s + { 7 n}i
(assign1)
(seq1)
dom(s)
if
hskip; e2 , si he2 , si
So
Slide 39
hl := 1; l := 2, {l 7 0}i
hskip; l := 2, {l 7 1}i
hskip, {l 7 2}i
Weve chosen
(assign1)
if
hv ; e2 , si he2 , si
(seq1)
dom(s)
Matter of taste? Another possiblity: return the old value, e.g. in ANSI C signal handler
installation.
Language design 3. Store initialization
Recall that
(deref)
Slide 40
(assign1)
h := n, si hskip, s + { 7 n}i
both require
if
dom(s)
Instead, could
1. implicitly initialize all locations to 0, or
2. allow assignment to an
20
In the next section we will introduce a type system to rule out any program that could reach
a stuck expression of these forms. (Would the two alternatives be a good idea?)
Language design 4. Storable values
Recall stores s are finite partial functions from L to Z, with rules:
(deref)
(assign1)
Slide 41
(assign2)
h := n, si hskip, s + { 7 n}i
if
dom(s)
he, si he , s i
h := e, si h := e , s i
hl := true, si is stuck.
Slide 42
no: theres no support for gadgets like functions, objects, lists, trees,
Slide 43
modules,.....
rather than let the program get stuck or give a runtime error. Well do
so with a type system.
2.2
Typing
Slide 44
L1 Typing
21
Type systems
used for
Type systems are also used to provide information to compiler optimizers; to enforce security
properties, from simple absence of buffer overflows to sophisticated information-flow policies;
and (in research languages) for many subtle properties, e.g. type systems that allow only
polynomial-time computation. There are rich connections with logic, which well return to
later.
Formal type systems
Slide 46
{}
{}
6 3 + false
: int
{}
: int
: T for any T
Note that the last is excluded despite the fact that when you execute the program you will
always get an int type systems define approximations to the behaviour of programs, often
quite crude and this has to be so, as we generally would like them to be decidable, so that
compilation is guaranteed to terminate.
Types for L1
Types of expressions:
Slide 47
22
Slide 48
e:T (1 of 3)
n:int for n Z
e1 :int
(op +)
e2 :int
e1 :int
e1 + e2 :int
(if)
(op )
e2 :int
e1 e2 :bool
e1 :bool e2 :T
e3 :T
if e1 then e2 else e3 :T
Note that in (if) the T is arbitrary, so long as both premises have the same T .
In some rules we arrange the premises vertically to save space, e.g.
(op +)
e1 :int
e2 :int
e1 + e2 :int
but this is merely visual layout. Derivations using such a rule should be written as if it was
in the horizontal form.
(op +) e1 :int e2 :int
e1 + e2 :int
Example
To show {}
Slide 49
(int)
{} false:bool
{} 2:int
{} if false then 2 else 3 + 4:int
where is
(int)
(op +)
(int)
{} 3:int
{} 4:int
{} 3 + 4:int
(assign)
Slide 50
(deref)
e:T (2 of 3)
() = intref e:int
:= e:unit
() = intref
!:int
23
e:T (3 of 3)
Slide 51
(seq)
(while)
skip:unit
e1 :unit e2 :T
e1 ; e2 :T
e1 :bool e2 :unit
while e1 do e2 :unit
Note that the typing rules are syntax-directed for each clause of the abstract syntax for
expressions there is exactly one rule with a conclusion of that form.
Properties
Theorem 2 (Progress) If
Slide 52
and he, si
From these two we have that well-typed programs dont get stuck:
Theorem 4 (Safety) If e:T , dom() dom(s), and
he, si he , s i then either e is a value or there exist e , s such
that he , s i he , s i.
Slide 53
e:T
e:T is
Second problem is usually harder than the first. Solving it usually results
in a type inference algorithm: computing a type T for a phrase e , given
type environment (or failing, if there is none).
For this type system, though, both are easy.
More Properties
Theorem 5 (Type inference) Given , e , one can find T such that
e:T .
Also:
Theorem 7 (Uniqueness of typing) If
T =T .
24
The file l1.ml contains also an implementation of a type inference algorithm for L1 take
a look.
Type inference Implementation
First must pick representations for types and for s:
datatype type L1 =
int
| unit
| bool
Slide 55
infertype :
In the semantics, type environments are partial functions from locations to the singleton
set {intref}. Here, just as we did for stores, we represent them as a list of loc*type loc
pairs containing, for each in the domain of the type environment, exactly one element of
the form (l,intref).
The Type Inference Algorithm
fun infertype gamma (Integer n) = SOME int
| infertype gamma (Boolean b) = SOME bool
| infertype gamma (Op (e1,opr,e2))
= (case (infertype gamma e1, opr, infertype gamma e2) of
(SOME int, Plus, SOME int) => SOME int
| (SOME int, GTEQ, SOME int) => SOME bool
=> NONE)
Slide 56
=> NONE)
=> NONE )
25
...
| infertype gamma (If (e1,e2,e3))
= (case (infertype gamma e1,
infertype gamma e2,
infertype gamma e3) of
(SOME bool, SOME t2, SOME t3) =>
Slide 57
=> NONE)
e1 :bool
e2 :T
(if)
e3 :T
if e1 then e2 else e3 :T
...
| infertype gamma (Deref l)
= (case lookup (gamma,l) of
SOME intref => SOME int
Slide 58
() = intref
!:int
Again, the code depends on a uniqueness property (Theorem 7), without which we would
have to have infertype return a type L1 list of all the possible types.
Executing L1 in Moscow ML
L1 is essentially a fragment of Moscow ML given a typable L1
expression e and an initial store s , e can be executed in Moscow ML by
wrapping it
let val skip = ()
and l1 = ref n1
and l2 = ref n2
Slide 59
..
and lk = ref nk
in
e
end;
26
type inference means you only have to write them where its useful
Some languages build the type system into the syntax. Original FORTRAN, BASIC etc.
had typing built into variable names, with e.g. those beginning with I or J storing integers). Sometimes typing is built into the grammar, with e.g. separate grammatical classes
of expressions and commands. As the type systems become more expressive, however, they
quickly go beyond what can be captured in context-free grammars. They must then be
separated from lexing and parsing, both conceptually and in implementations.
27
2.3
Syntax
Booleans b B = {true, false}
Integers n Z = {..., 1, 0, 1, ...}
Locations L = {l , l0 , l1 , l2 , ...}
Operations op ::= + |
Expressions
e
::=
n | b | e1 op e2 | if e1 then e2 else e3 |
:= e |! |
skip | e1 ; e2 |
while e1 do e2
Operational semantics
Note that for each construct there are some computation rules, doing real work, and some
context (or congruence) rules, allowing subcomputations and specifying their order.
Say stores s are finite partial functions from L to Z. Say values v are expressions from the
grammar v ::= b | n | skip.
(op +)
hn1 + n2 , si hn, si
if n = n1 + n2
(op )
hn1 n2 , si hb, si
if b = (n1 n2 )
(op1)
(op2)
he1
he1 , si he1 , s i
op e2 , si he1 op e2 , s i
he2 , si he2 , s i
hv op e2 , si hv op e2 , s i
if dom(s)
he, si he , s i
h := e, si h := e , s i
(seq1) hskip; e2 , si he2 , si
(seq2)
he1 , si he1 , s i
he1 ; e2 , si he1 ; e2 , s i
hif e1 then e2
he1 , si he1 , s i
else e3 , si hif e1 then e2 else e3 , s i
(while)
hwhile e1 do e2 , si hif e1 then (e2 ; while e1 do e2 ) else skip, si
28
Typing
Types of expressions:
T
::=
Types of locations:
Tloc
::=
intref
Write T and Tloc for the sets of all terms of these grammars.
Let range over TypeEnv, the finite partial functions from locations L to Tloc .
(int) n:int
(bool) b:bool
(op +)
for n Z
e1 :int
e2 :int
e1 + e2 :int
(op )
e1 :int
e2 :int
e1 e2 :bool
e2 :T
e3 :T
(if) e1 :bool
if e1 then e2 else e3 :T
(assign)
(deref)
() = intref
e:int
:= e:unit
() = intref
!:int
(skip) skip:unit
e2 :T
(seq) e1 :unit
e1 ; e2 :T
e2 :unit
(while) e1 :bool
while e1 do e2 :unit
29
2.4
Exercises
Exercise 1 Write a program to compute the factorial of the integer initially in location
l1 . Take care to ensure that your program really is an expression in L1.
Exercise 2 Give full derivations of all the reduction steps of
h(l0 := 7); (l1 := (!l0 + 2)), {l0 7 0, l1 7 0}i
Exercise 3 Give full derivations of the first four reduction steps of the he, si of the first
L1 example on Slide 16.
Exercise 4 Adapt the implementation code to correspond to the two rules (op1b) and
(op2b) on Slide 38. Give some test cases that distinguish between the original and the new
semantics.
Exercise 5 Adapt the implementation code to correspond to the two rules (assign1) and
(seq1) on Slide 39. Give some test cases that distinguish between the original and the new
semantics.
Exercise 6 Fix the L1 semantics to match the implementation, taking care with the
representation of integers.
Exercise 7 Give a type derivation for (l0 := 7); (l1 := (!l0 +2)) with = l0 :intref, l1 :intref.
Exercise 8 Give a type derivation for e on Slide 26 with = l1 :intref, l2 :intref, l3 :intref .
Exercise 9 Does Type Preservation hold for the variant language with rules (assign1)
and (seq1)? on Slide 39? If not, give an example, and show how the type rules could be
adjusted to make it true.
Exercise 10 Adapt the type inference implementation to match your revised type system
from Exercise 9.
Exercise 11 Check whether mosml, the L1 implementation and the L1 semantics agree
on the order of evaluation for operators and sequencing.
30
Induction
Induction
Slide 61
Weve stated several theorems, but how do we know they are true?
Intuition is often wrong we need proof.
Use proof process also for strengthening our intuition about subtle
Slide 62
language features, and for debugging definitions it helps you examine all
the various cases.
Most of our definitions are inductive. To prove things about them, we need
the corresponding induction principles.
Three forms of induction
Prove facts about all natural numbers by mathematical induction.
Prove facts about all terms of a grammar (e.g. the L1 expressions) by
Slide 63
structural induction.
Prove facts about all elements of a relation defined by rules (e.g. the L1
transition relation, or the L1 typing relation) by rule induction.
We shall see that all three boil down to induction over certain trees.
Principle of Mathematical Induction
For any property (x ) of natural numbers x
prove
Slide 64
N = {0, 1, 2, ...}, to
x N.(x )
its enough to prove
(0) ( x N.(x ) (x + 1)) x N.(x )
31
(0) ( x N.(x ) (x + 1)) x N.(x )
Slide 65
1 + 2 + ... + x = 1/2 x (x + 1)
(state explicitly)
def
by mathematical induction.
Base case:
(conjunct (0) )
(instantiate )
(0) is (1 + ... + 0 = 1/2 0 (0 + 1)), which holds as both sides are equal to 0.
Inductive step:
(conjunct x N.(x ) (x + 1) )
Consider an arbitrary k N
(its a universal (), so consider an arbitrary one) .
Suppose (k )
(to show the implication (k ) (k + 1), assume the premise and try
show the conclusion) .
We have to show (k + 1), i.e.
(state what we have to show explicitly)
to
=
=
(1 + 2 + ... + k ) + (k + 1)
(1/2 k (k + 1)) + (k + 1)
(rearranging)
(using (k ) )
(say where you use the induction hypothesis assumption (k ) made above)
=
=
=
1/2 (k (k + 1) + (k + 1) 1 + 1 k + 1)
1/2 k (k + 1) + 1/2 ((k + 1) + k + 1)
1/2 k (k + 1) + (k + 1)
32
3.1
Slide 66
he1 , s1 i and
he, si he2 , s2 i then he1 , s1 i = he2 , s2 i .
First, dont forget the elided universal quantifiers.
Theorem 1 (Determinacy) For all e, s, e1 , s1 , e2 , s2 , if
Slide 67
:= e |! |
skip
| e; e |
while
e do e
f, , !, l, ..];
Slide 68
if then else
tt
tt
tt
skip
;
skip l :=
!l 0
2 + 2 6= 4
4
2
1 + 2 + 3 ambiguous
Slide 69
(1 + 2) + 3 6= 1 + (2 + 3)
3
+
+
2
Parentheses are only used for disambiguation they are not part of the
grammar.
33
For semantics we dont want to be distracted by concrete syntax its easiest to work
with abstract syntax trees, which for this grammar are finite trees, with ordered branches,
labelled as follows:
leaves (nullary nodes) labelled by B Z ({!} L) {skip} = {true, false, skip}
{..., 1, 0, 1, ...} {!l , !l1 , !l2 , ...}.
unary nodes labelled by {l :=, l1 :=, l2 :=, ...}
binary nodes labelled by {+, , ; , while do }
ternary nodes labelled by {if then else }
Abstract grammar suggests a concrete syntax we write expressions as strings just for
convenience, using parentheses to disambiguate where required and infix notation, but really
mean trees.
Principle of Structural Induction (for abstract syntax)
For any property (e) of expressions e , to prove
e L1 .(e)
its enough to prove for each tree constructor c (taking k
Slide 70
0 arguments)
that if holds for the subtrees e1 , .., ek then holds for the tree
where the tree constructors (or node labels) c are n , true, false, !l , skip,
(skip)
b {true, false}.(b)
n Z.(n)
Slide 71
L.(!)
L. e.(e) ( := e)
unary:
binary:
ternary:
he1 , s1 i and
he, si he2 , s2 i then he1 , s1 i = he2 , s2 i .
Take
Slide 72
def
(e) = s, e , s , e , s .
(he, si he , s i he, si he , s i)
he , s i = he , s i
and show
To do that we need to verify all the premises of the principle of structural induction the
formulae in the second box below for this .
34
def
(e)
s, e , s , e , s .
(he, si he , s i he, si he , s i)
he , s i = he , s i
nullary:
(skip)
b {true, false}.(b)
Slide 73
n Z.(n)
L.(!)
unary:
binary:
L. e.(e) ( := e)
op . e1 , e2 .((e1 ) (e2 )) (e1 op e2 )
e1 , e2 .((e1 ) (e2 )) (e1 ; e2 )
e1 , e2 .((e1 ) (e2 )) (while e1 do e2 )
ternary:
3.2
Slide 74
Theorem 2 (Progress) If
he , s i really mean?
Inductive Definitions
(op +)
hn1 + n2 , si hn, si
if n
= n1 + n2
Slide 75
(op1)
(op +)
he1 , si he1 , s i
he1 op e2 , si he1 op e2 , s i
e1 :int e2 :int
e1 + e2 :int
35
Slide 76
= n1 + n2 ) and of e1 , e2 , s, e1 , s .
Slide 77
(op+ )
(op1)
(op + )
(op1)
Note the last has a premise that is not itself derivable, but nonetheless this is a legitimate
instance of (op1).
Now a derivation of a transition he, si
he , s i or typing judgment
e:T is a finite tree such that each step is a concrete rule instance.
Slide 78
(op+)
h2 + 2, {}i h4, {}i
(op1)
h(2 + 2) + 3, {}i h4 + 3, {}i
(op1)
h(2 + 2) + 3 5, {}i h4 + 3 5, {}i
(deref)
(int)
!l :int
2:int (op +)
(int)
(!l + 2):int
3:int
(op +)
(!l + 2) + 3:int
a SR .(a)
Slide 79
its enough to prove that {a
concrete rule instance
..
c
36
hk
For some proofs a slightly different principle is useful this variant allows you to assume
each of the hi are themselves members of SR .
Principle of rule induction (a slight variant)
For any property (a) of elements a of A, and any set of rules which
inductively define the set SR , to prove
a SR .(a)
Slide 80
its enough to prove that
..
c
h1
if (h1 ) ... (hk ) h1
hk
SR .. hk SR then (c).
(This is just the original principle for the property ((a) a SR ).)
Proving Progress (Outline)
Theorem 2 (Progress) If
Slide 81
Proof Take
def
(, e, T ) = s. dom() dom(s)
value(e) ( e , s .he, si
he , s i)
Principle of Rule Induction (variant form): to prove (a) for all a in the
set SR , its enough to prove that for each concrete rule instance
h1
if (h1 ) ... (hk ) h1
..
c
hk
SR .. hk SR then (c).
Slide 82
(int)
, n.(, n, int)
(deref)
(op +)
(seq)
(, e1 + e2 , int)
(, e1 ; e2 , T )
etc.
(deref)
(int)
!l :int
2:int (op +)
(int)
(!l + 2):int
3:int
(op +)
(!l + 2) + 3:int
37
Slide 84
3.3
Example proofs
Example Proofs
In the notes there are detailed example proofs for Determinacy (structural
Slide 85
Slide 86
Remember the point is to use the mathematics to help you think about things that are too
complex to keep in your head all at once: to keep track of all the cases etc. To do that, and
to communicate with other people, its important to write down the reasoning and proof
structure as clearly as possible. After youve done a proof you should give it to someone
(your supervision partner first, perhaps) to see if they (a) can understand what youve said,
and (b) if they believe it.
Sometimes it seems hard or pointless to prove things because they seem
too obvious....
1. proof lets you see (and explain) why they are obvious
Slide 87
38
Take
(e)
def
s, e , s , e , s .(he, si he , s i he, si he , s i) he , s i = he , s i
dom(s)
e = s()
s = s
so e = e and s = s .
Case := e. Suppose (e) (then we have to show ( := e)).
Take arbitrary s, e , s , e , s such that h := e, si he , s i h := e, si
he , s i.
Its handy to have this lemma:
Lemma 9 For all e L1 , if e is a value then s. e , s .he, si
he , s i.
Proof
By defn e is a value if it is of one of the forms n, b, skip. By
examination of the rules on slides ..., there is no rule with conclusion
of the form he, si he , s i for e one of n, b, skip.
The only rules which could be applicable, for each of the two transitions, are
(assign1) and (assign2).
case h := e, si he , s i is an instance of (assign1). Then for some n we have
e = n and dom(s) and e = skip and s = s + { 7 n}.
39
Then for some e1 and e1 we have he1 , si he1 , s i (*), he1 , si he1 , s i
(**), e = e1 op e2 , and e = e1 op e2 . Now, by the induction hypothesis
(e1 ), (*) and (**) we have he1 , s i = he1 , s i, so he , s i = he1 op e2 , s i =
he1 op e2 , s i = he , s i as required.
he1 ; e2 , si
e1
do
e2 , si he , s i
40
(note that the level of written detail can vary, as here if you and the reader agree but you
must do all the steps in your head. If in any doubt, write it down, as an aid to thought...!)
Lemma: Values dont reduce
L1 , if e is a value then
s. e , s .he, si he , s i.
Lemma 10 For all e
Slide 88
Proof
41
Theorem 2 (Progress) If e:T and dom() dom(s) then either e is a value or there
exist e , s such that he, si he , s i.
Proof
Take
def
for n Z
It has no premises, so we have to show that for all instances , e, T of the conclusion we have (, e, T ).
For any such instance, there must be an n Z for which e = n.
Now is of the form s.dom() dom(s) ..., so consider an arbitrary s and
assume dom() dom(s).
e1 :int
e2 :int
(op +)
e1 + e2 :int
Now the first disjunct is false (e1 + e2 is not a value), so we have to show the
second, i.e.he , s i.he1 + e2 , si he , s i.
By (*) one of the following holds.
case e1 , s .he1 , si he1 , s i.
case e2 is a value.
(Now want to use (op+ ), but need to know that e1 and e2 are really
integers. )
Lemma 11 for all , e, T , if e:T , e is a value and T = int then for
some n Z we have e = n.
Proof By rule induction. Take (, e, T ) = ((value(e) T = int)
n Z.e = n).
Case (int). ok
42
e1 :bool
e2 :T
e3 :T
(if)
if e1 then e2 else e3 :T
case e1 is a value.
(Now want to use (if1) or (if2), but need to know that e1 {true, false}.
Realize should have proved a stronger Lemma above).
Lemma 12 For all , e, T . if e:T and e is a value, then T = int
n Z.e = n, T = bool b {true, false}.e = b, and T = unit
e = skip.
Proof
() = intref
!:int
Slide 89
43
Now prove the first part, ie If e:T and dom() dom(s) and he, si he , s i
then e :T .
Prove by rule induction on derivations of he, si he , s i.
dom() dom(s)) e :T .
if n = n1 + n2
he1
he1 , si he1 , s i
op e2 , si he1 op e2 , s i
Slide 90
3.4
Determinacy
Progress
Type Preservation
e:T
he , s i
Safety
mathematical induction on k
Uniqueness of typing
...
Decidability of typability
exhibiting an algorithm
Decidability of checking
Exercises
You should be able to prove all the theorems about L1 independently. These exercises are
to get you started.
Exercise 12 Without looking at the proof in the notes, do the cases of the proof of Theorem 1 (Determinacy) for e1 op e2 , e1 ; e2 , while e1 do e2 , and if e1 then e2 else e3 .
Exercise 13 Try proving Determinacy for the language with nondeterministic order of
evaluation for e1 op e2 (ie with both (op1) and (op1b) rules), which is not determinate.
Explain where exactly the proof cant be carried through.
Exercise 14 Complete the proof of Theorem 2 (Progress).
Exercise 15 Complete the proof of Theorem 3 (Type Preservation).
Exercise 16 Give an alternate proof of Theorem 3 (Type Preservation) by rule induction over type derivations.
Exercise 17 Prove Theorem 7 (Uniqueness of Typing).
45
Functions
Functions L2
Slide 91
<script type="text/vbscript">
function addone(x)
addone = x+1
end function
</script>
Slide 93
class M {
public static void Main() {
IntThunk[] funcs = new IntThunk[11];
for (int i = 0; i <= 10; i++)
{
funcs[i] = delegate() { return i; };
}
foreach (IntThunk f in funcs)
{
System.Console.WriteLine(f());
}
}
}
Most languages have some kind of function, method, or procedure some way of abstracting
a piece of code on a formal parameter so that you can use the code multiple times with
different arguments, without having to duplicate the code in the source. The next two
lectures explore the design space for functions, adding them to L1.
46
Functions Examples
We will add expressions like these to L1.
(fn x:int x + 1)
(fn x:int x + 1) 7
Slide 94
For simplicity, well deal with anonymous functions only. Functions will always take a single
argument and return a single result though either might itself be a function or a tuple.
Functions Syntax
First, extend the L1 syntax:
Variables x
Expressions
Slide 95
e ::= ... | fn x :T e | e1 e2 | x
Types
Concrete syntax. By convention, application associates to the left, so e1 e2 e3 denotes (e1 e2 ) e3 , and type arrows associate to the right, so T1 T2 T3 denotes
T1 (T2 T3 ). A fn extends to the right as far as parentheses permit, so fn x:unit x; x
denotes fn x:unit (x; x), not (fn x:unit x); x. These conventions work well for functions
that take several arguments, e.g.fn x:unit fn y:int x; y has type unit int int, and
we can fully apply it simply by juxtaposing it with its two arguments
(fn x:unit fn y:int x; y) skip 15.
Variables are not locations ( L X = {} ), so x := 3 is not in the syntax.
You cant abstract on locations. For example, (fn l :intref !l ) is not in the syntax.
The (non-meta) variables x, y, z are not the same as metavariables x , y, z . In the notes
they are distinguished by font; in handwriting one just have to keep track in your head
not often a problem.
These expressions look like lambda terms (and fn x:int x could be written x:int.x).
But, (a) were adding them to a rich language, not working with the pure lambda
calculus (cf. Foundations of Functional Programming), and (b) were going to explore
several options for how they should behave.
Type-directed language design. This type grammar (and expression syntax) suggests
the language will include higher-order functions you can abstract on a variable of any
type, including function types. If you only wanted first-order functions, youd say
A
T
Tloc
::=
::=
::=
Note that first-order function types include types like int (int int) and int (int (int int)),
of functions that take an argument of base type and return a (first-order) function, e.g.
(fn y:int (fn x:int x + y))
47
Some languages go further, forbidding partial application. Well come back to this.
4.1
In order to express the semantics for functions, we need some auxiliary definitions.
Variable shadowing
{int y; ...
} // Static error
...
{int y; ...
...
}
}
Variable shadowing is not allowed in Java. For large systems that would be a problem, eg
in a language with nested function definitions, where you may wish to write a local function
parameter without being aware of what is in the surrounding namespace.
Alpha conversion
In expressions fn
x :T e the x is a binder.
inside e , any x s (that arent themselves binders and are not inside
another fn x :T ...) mean the same thing the formal parameter
of this function.
Slide 97
example, fn
fn
Slide 98
x+y
fn
fn
x:int x + 2
x:int x + z
if y then
x :T ....
Note that in fn x:int 2 the x is not an occurrence. Likewise, in fn x:int x + 2 the left
x is not an occurrence; here the right x is an occurrence that is bound by the left x.
48
Slide 99
fn
fn x:int
x+2
fn x:int
x+z
fn y:int
y+z
fn z:int
z+z
x:int (fn x:int
x+2)
Slide 100
For example:
fn x:int
x+z = fn y:int
y+z 6= fn z:int
z+z
x:int x + z
fn
y:int y + z
6=
z:int z + z
fn
Slide 101
x:int
tt
tt
t
t
fn
y:int
fn
z:int
x :T node);
= :int
+
tt
tt
t
t
fn
= :int
+
tt
tt
t
t
49
fn
= :inta
tt
tt
t
t
fn
fn
:int
fn 8 :int
fn
= :int
fn
(fn x:int x) 7
Slide 103
fn
@
tt
ttt
B :int
:int
fn
:int
int int
6
fn
:int
] k
De Bruijn indices
Our implementation will use those pointers known as De Bruijn indices.
Each occurrence of a bound variable is represented by the number of
fn
fn
Slide 104
fn
:int
fn 8 :int
fn
= :int
fn
:int
Free Variables
Say the free variables of an expression e are the set of variables x for
which there is an occurence of x free in e .
fv(x )
Slide 105
= {x }
= fv(e1 ) fv(e2 )
fv(e1
op e2 )
fv(fn
x :T e) = fv(e) {x }
= {}.
eE
fv(e).
50
For example
fv(x + y)
fv(fn x:int x + y)
fv(x + (fn x:int x + y)7)
=
=
=
{x, y}
{y}
{x, y}
=
=
=
=
=
=
=
=
=
=
=
=
{x }
fv(e) {x }
fv(e1 ) fv(e2 )
{}
fv(e1 ) fv(e2 )
fv(e1 ) fv(e2 ) fv(e3 )
{}
{}
fv(e)
{}
fv(e1 ) fv(e2 )
fv(e1 ) fv(e2 )
The semantics for functions will involve substituting actual parameters for formal parameters.
Substitution Examples
The semantics for functions will involve substituting actual parameters for
formal parameters.
Write {e/x }e for the result of substituting e for all free occurrences of x
Slide 106
in e . For example
{3/x}(x x)
= (3 3)
Note that substitution is a meta-operation its not part of the L2 expression grammar.
The notation used for substitution varies people write {3/x }e, or [3/x ]e, or e[3/x ], or
{x 3}e, or...
Substitution Definition
Defining that:
{e/z }x
Slide 107
= e
if x
= x
otherwise
...
if x
=z
6= z (*)
and x
/ fv(e) (*)
51
x :T e1 to
{y + 2/x}(fn y:int x + y)
Slide 108
= fn y :int (y + 2) + y
Slide 109
Write dom() for the set of variables in the domain of ; ran() for the set of expressions
in the range of , ie
dom({e1 /x1 , .., ek /xk }) = {x1 , .., xk }
ran({e1 /x1 , .., ek /xk }) = {e1 , .., ek }
Define the application of simultaneous substitution to a term by:
if x dom()
otherwise
=
=
(x )
x
(fn x :T e)
(e1 e2 )
n
(e1 op e2 )
(if e1 then e2 else e3 )
(b)
(skip)
( := e)
(!)
(e1 ; e2 )
(while e1 do e2 )
=
=
=
=
=
=
=
=
=
=
=
fn x :T ( e)
if x
/ dom() and x
/ fv(ran()) (*)
( e1 )( e2 )
n
(e1 ) op (e2 )
if (e1 ) then (e2 ) else (e3 )
b
skip
:= (e)
!
(e1 ); (e2 )
while (e1 ) do (e2 )
4.2
Function Behaviour
Function Behaviour
Consider the expression
Slide 110
52
Slide 111
hskip; skip
hskip
, {l = 2}i
, {l = 1}i
, {l = 1}i
This is a common design choice ML, Java. It is a strict semantics fully evaluating the
argument to function before doing the application.
L2 Call-by-value
Values v
::= b | n | skip | fn x :T e
(app1)
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
Slide 112
(app2)
(fn)
he2 , si he2 , s i
hv e2 , si hv e2 , s i
Slide 113
The rules for these constructs dont touch the store. In a pure functional language,
configurations would just be expressions.
A naive implementation of these rules would have to traverse e and copy v as many
times as there are free occurrences of x in e. Real implementations dont do that,
using environments instead of doing substitution. Environments are more efficient;
substitutions are simpler to write down so better for implementation and semantics
respectively.
53
; l := 2, {l 7 1}i
hskip
, {l 7 2}i
hl := 2
, {l 7 1}i
Slide 115
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
h(fn x :T e)e2 , si h{e2 /x }e, si
(CBN-fn)
h{l := 2/x}skip
hskip
, {l 7 0}i
, {l 7 0}i
Slide 116
(beta-app1)
Slide 117
(beta-app2)
(beta-fn1)
(beta-fn2)
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
he2 , si he2 , s i
he1 e2 , si he1 e2 , s i
h(fn x :T e)e2 , si h{e2 /x }e, si
he, si he , s i
hfn x :T e, si hfn x :T e , s i
This reduction relation includes the CBV and CBN relations, and also reduction inside
lambdas.
54
L2 Beta: Example
(fn x:int x + x)
(2
+ 2)
Slide 118
(fn x:int x + x) 4
(2 + 2) + (2 + 2)
)
u
4 + (2 + 2)
(2
+ 2) + 4
$
r
4+4
8
Function Behaviour. Choice 4: Normal-order reduction
Slide 119
What will (fn x:unit skip) (while true do skip) do in the different semantics?
What about (fn x:unit skip) ( :=! + 1)?
Slide 120
Purity
Without strict, call-by-value semantics, it becomes hard to understand what order your code
is going to be run in. Non-strict languages typically dont allow unrestricted side effects (our
combination of store and CBN is pretty odd ). Haskell encourages pure programming, without
effects (store operations, IO, etc.) except where really necessary. Where they are necessary,
it uses a fancy type system to give you some control of evaluation order.
For a pure language, Call-By-Name gives the same results as Call-By-Need, which is more
efficient. The first time the argument evaluated we overwrite all other copies by that value.
Call-By-Need Example (Haskell)
(enumFrom (n+1))
sieve (x:xs) =
x :
in
Slide 121
sieve (enumFrom 2)
==>
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
59,61,67,71,73,79,83,89,97,101,103,107,109,
113,127,131,137,139,149,151,157,163,167,173,
179,181,191,193,197,199,211,223,227,229,233,
,,Interrupted!
Slide 122
55
4.3
Function Typing
Typing functions (1)
Before, gave the types of store locations; it ranged over TypeEnv
which was the set of all finite partial functions from locations L to Tloc .
Now, it must also give assumptions on the types of variables: e.g.
Slide 123
Type environments,
dom().() Tloc
x dom().(x ) T
Notation: if x
Slide 124
(fn)
(app)
x :T
if (x )
=T
, x :T e:T
fn x :T e : T T
e1 :T T
e2 :T
e1 e2 :T
Typing functions Example
Slide 125
(var)
(int)
x:int x:int
x:int 2:int (op+)
x:int x + 2:int
(fn)
(int)
{} (fn x:int x + 2):int int
{} 2:int
(app)
{} (fn x:int x + 2) 2:int
Note that sometimes you need the alpha convention, e.g. to type
fn x:int x + (fn x:bool if x then 3 else 4)true
Its a good idea to start out with all binders different from each other and from all
free variables. It would be a bad idea to prohibit variable shadowing like this in source
programs.
In ML you have parametrically polymorphic functions, e.g. (fn x: x): , but
we wont talk about them here thats in Part II Types.
Another example:
(int)
l :intref, x:unit 1:int
(asn)
(var)
l :intref, x:unit (l := 1):unit
l :intref, x:unit x:unit
(seq)
(int)
l :intref, x:unit (l := 1); x:unit
l :intref 2:int
(fn)
(asn)
l :intref (fn x:unit (l := 1); x):unit unit
l :intref (l := 2):unit
(app)
l :intref (fn x:unit (l := 1); x) (l := 2):unit
56
Properties of Typing
We only consider executions of closed programs, with no free variables.
Theorem 15 (Progress) If e closed and
Slide 126
e:T and
(4))
e:T and
dom(s) and he, si he , s i then e :T and e
closed and dom() dom(s ).
Theorem 16 (Type Preservation) If e closed and
dom()
e:T and
dom(s) and he, si he , s i then e :T and e
closed and dom() dom(s ).
Theorem 16 (Type Preservation) If e closed and
dom()
Taking
(e, s, e , s ) =
Slide 127
, T .
e:T
e :T
we show
induction.
closed(e) dom()
dom(s)
closed(e ) dom()
dom(s )
Slide 128
Slide 129
store operations, if
See Pierce Ch.12 (the details are not in the scope of this course).
57
4.4
Slide 130
(fn x :T e2 )e1
x :T = e1 in e2 end
(let)
(fn x :T e2 )e1
x :T = e1 in e2 end
e1 :T
, x :T e2 :T
let val x :T = e1 in e2 end:T
Slide 131
(let1)
he1 , si he1 , s i
hlet val x :T = e1 in e2 end, si hlet val x :T = e1 in e2 end, s i
(let2)
Our alpha convention means this really is a local definition there is no way to refer to the
locally-defined variable outside the let val .
x + let val x:int = x in (x + 2) end =
Slide 132
x:int int =
x 3 end
58
But...
What about
let val rec
x = (x, x) in x end ?
Slide 133
T = T 1 T2
e
Slide 134
= fn y:T1 e1
y:T e1 ) and in e2 )
Slide 135
{(fn y:T1 let val rec x :T1 T2 = (fn y:T1 e1 ) in e1 end)/x }e2
59
For example:
let val rec x:int int =
(fn y:int if y 1 then y + (x(y + 1)) else 0)
in
x3
end
(letrecfn)
fn y:int
let val rec x:int int =
(fn y:int if y 1 then y + (x(y + 1)) else 0)
in
if y 1 then y + (x(y + 1)) else 0
end 3
(app)
let val rec x:int int =
(fn y:int if y 1 then y + (x(y + 1)) else 0)
in
if 3 1 then 3 + (x(3 + 1)) else 0)
end
(letrecfn)
if 3 1 then
3 + ( fn y:int
let val rec x:int int =
(fn y:int if y 1 then y + (x(y + 1)) else 0)
in
if y 1 then y + (x(y + 1)) else 0
end (3 + 1))
else
0
...
Recursive Functions Minimization Example
Below, in the context of the let val rec , x
for which f
n evaluates to some m 0.
Slide 136
let val
f:int int
xf0
end
end
As a test case, we apply it to the function (fn z :int if z 3 then (if 3 z then 0 else 1) else 1),
which is 0 for argument 3 and 1 elsewhere.
60
Do we need while
e1 do e2 ?
Slide 137
let val rec
fn
e1 do e2
w:unit unit =
in
w skip
end
for fresh w and y not in fv(e1 ) fv(e2 ).
In each case typing is the same. Reduction is essentially the same we will be able to
make this precise when we study contextual equivalence.
4.5
Implementation
Implementation
Slide 138
The implementation lets you type in L2 expressions and initial stores and watch them
resolve, type-check, and reduce.
Implementation Scope Resolution
61
Implementation Substitution
Slide 140
Slide 141
SOME (e1,s)=>SOME(App(e1,e2),s)
| NONE => NONE ))
Implementation Type Inference
type typeEnv
= (loc*type loc) list * type expr list
inftype gamma (Var n) = nth (#2 gamma) n
inftype gamma (Fn (t,e))
Slide 142
=> NONE )
62
Implementation Closures
Naively implementing substitution is expensive. An efficient
implementation would use closures instead cf. Compiler Construction.
Slide 143
example
Slide 144
hn, si hn, si
he , s i.
hv , s i, for
63
4.6
Syntax
Booleans b B = {true, false}
Integers n Z = {..., 1, 0, 1, ...}
Locations L = {l , l0 , l1 , l2 , ...}
Variables x X for a set X = {x, y, z, ...}
Operations op ::= + |
Types
T
Tloc
::=
::=
Expressions
e
::=
n | b | e1 op e2 | if e1 then e2 else e3 |
:= e |! |
skip | e1 ; e2 |
while e1 do e2 |
fn x :T e | e1 e2 | x |
let val x :T = e1 in e2 end|
let val rec x :T1 T2 = (fn y:T1 e1 ) in e2 end
hn1 + n2 , si hn, si
if n = n1 + n2
(op )
hn1 n2 , si hb, si
if b = (n1 n2 )
(op1)
(op2)
he1
he1 , si he1 , s i
op e2 , si he1 op e2 , s i
he2 , si he2 , s i
hv op e2 , si hv op e2 , s i
he, si he , s i
h := e, si h := e , s i
(seq1) hskip; e2 , si he2 , si
(seq2)
he1 , si he1 , s i
he1 ; e2 , si he1 ; e2 , s i
64
if dom(s)
he1 , si he1 , s i
else e3 , si hif e1 then e2 else e3 , s i
hif e1 then e2
(while)
hwhile e1 do e2 , si hif e1 then (e2 ; while e1 do e2 ) else skip, si
(app1)
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
(app2)
he2 , si he2 , s i
hv e2 , si hv e2 , s i
he1 , si he1 , s i
end, si hlet val x :T = e1 in e2 end, s i
(let2)
hlet val x :T = v in e2 end, si h{v /x }e2 , si
(letrecfn) let val rec x :T1 T2 = (fn y:T1 e1 ) in e2 end
{(fn y:T1 let val rec x :T1 T2 = (fn y:T1 e1 ) in e1 end)/x }e2
Typing
Type environments, TypeEnv2, are finite partial functions from L X to Tloc T
such that
dom().() Tloc
x dom().(x ) T
(int) n:int
(bool) b:bool
(op +)
for n Z
e1 :int
e2 :int
e1 + e2 :int
(op )
e1 :int
e2 :int
e1 e2 :bool
e2 :T
e3 :T
(if) e1 :bool
if e1 then e2 else e3 :T
(assign)
(deref)
() = intref
e:int
:= e:unit
() = intref
!:int
65
(skip) skip:unit
e2 :T
(seq) e1 :unit
e1 ; e2 :T
e2 :unit
(while) e1 :bool
while e1 do e2 :unit
(var) x :T
(fn)
if (x ) = T
, x :T e:T
fn x :T e : T T
e2 :T
(app) e1 :T T
e1 e2 :T
(let)
e1 :T
, x :T e2 :T
let val x :T = e1 in e2 end:T
66
4.7
Exercises
67
Data
Data L3
Slide 145
So far we have only looked at very simple basic data types int, bool, and unit, and functions
over them. We now explore more structured data, in as simple a form as possible, and revisit
the semantics of mutable store.
5.1
The two basic notions are the product and the sum type.
The product type T1 T2 lets you tuple together values of types T1 and T2 so for example
a function that takes an integer and returns a pair of an integer and a boolean has type
int (int bool). In C one has structs; in Java classes can have many fields.
The sum type T1 + T2 lets you form a disjoint union, with a value of the sum type either
being a value of type T1 or a value of type T2 . In C one has unions; in Java one might
have many subclasses of a class (see the l1.java representation of the L1 abstract syntax,
for example).
In most languages these appear in richer forms, e.g. with labelled records rather than simple
products, or labelled variants, or ML datatypes with named constructors, rather than simple
sums. Well look at labelled records in detail, as a preliminary to the later lecture on
subtyping.
Many languages dont allow structured data types to appear in arbitrary positions e.g. the
old C lack of support for functions that return structured values, inherited from close-tothe-metal early implementations. They might therefore have to have functions or methods
that take a list of arguments, rather than a single argument that could be of product (or
sum, or record) type.
Products
Slide 146
T ::= ... | T1 T2
e
Design choices:
pairs, not arbitrary tuples have int (int int) and (int int) int, but (a) theyre
different, and (b) we dont have (int int int). In a full language youd likely allow
(b) (and still have it be a different type from the other two).
have projections #1 and #2, not pattern matching fn (x , y) e. A full language
should allow the latter, as it often makes for much more elegant code.
dont have #e e (couldnt typecheck!).
68
Products typing
(pair)
Slide 147
(proj1)
(proj2)
e1 :T1
e2 :T2
(e1 , e2 ):T1 T2
e:T1 T2
#1 e:T1
e:T1 T2
#2 e:T2
Products reduction
Slide 148
(pair2)
(proj1)
(proj3)
he1 , si he1 , s i
h(e1 , e2 ), si h(e1 , e2 ), s i
he2 , si he2 , s i
h(v1 , e2 ), si h(v1 , e2 ), s i
h#1(v1 , v2 ), si hv1 , si (proj2) h#2(v1 , v2 ), si hv2 , si
he, si he , s i
h#1 e, si h#1 e , s i
(proj4)
he, si he , s i
h#2 e, si h#2 e , s i
T ::= ... | T1 + T2
Slide 149
case
Here we diverge slightly from Moscow ML syntax our T1 + T2 corresponds to the Moscow
ML (T1,T2) Sum in the context of the declaration
datatype (a,b) Sum = inl of a | inr of b;
Sums typing
e:T1
(inl)
inl e:T1 + T2 :T1 + T2
(inr)
Slide 150
e:T2
inr e:T1 + T2 :T1 + T2
e:T1 + T2
, x :T1 e1 :T
(case)
, y:T2 e2 :T
69
Slide 151
To maintain the unique typing property. Otherwise
3:int + int
inl
and
inl
3:int + bool
You might instead have a compiler use a type inference algorithm that can infer them,
or require every sum type in a program to be declared, each with different names for the
constructors inl , inr (cf OCaml).
Sums reduction
he, si he , s i
hinl e:T , si hinl e :T , s i
he, si he , s i
Slide 152
(case1)
(case2)
(inr)
he, si he , s i
hinr e:T , si hinr e :T , s i
Slide 153
type
constructors
T T
fn
x :T
T T
(, )
T +T
inl (
bool
true
inr (
false
70
destructors
e
#1
) case
if
#2
, x :T x :T
(var)
, P P
, x :T e:T
fn x :T e : T T
(fn)
Slide 154
(app)
e1 :T T e2 :T
e1 e2 :T
, P P
P P
P P P
P
(var)
(fn)
Slide 155
, P P
, x :T e:T
, P P
fn x :T e : T T
P P
(app)
e1 :T T
e2 :T
e1 e2 :T
P P
P
(pair)
e1 :T1
e2 :T2
(e1 , e2 ):T1 T2
P1 P2
P1 P2
(proj1)
(inl)
e:T1 T2
#1 e:T1
(proj2)
e:T1 T2
#2 e:T2
P1 P2
P1
P1 P2
P2
P1
P1 P2
e:T1
inl e:T1 + T2 :T1 + T2
The typing rules for a pure language correspond to the rules for a natural deduction calculus.
5.2
Slide 156
In L3 you cannot define IntList. It involves recursion at the type level (e.g. types for binary
trees). Making this precise is beyond the scope of this course.
71
Records
A generalization of products.
Take field labels
Slide 157
Labels lab
(where in each record (type or expression) no lab occurs more than once)
Note:
Labels are not the same syntactic class as variables, so (fn x:T {x = 3}) is not an
expression.
In ML a pair (true, fn x:int x) is syntactic sugar for a record {1 = true, 2 = fn x:int x}.
Note that #lab e is not an application, it just looks like one in the concrete syntax.
Again we will choose a left-to-right evaluation order for consistency.
Records typing
(record)
Slide 158
e1 :T1 .. ek :Tk
{lab 1 = e1 , .., lab k = ek }:{lab 1 :T1 , .., lab k :Tk }
(recordproj)
Here the field order matters, so (fn x:{1 :int, 2 :bool} x){2 = true, 1 = 17} does
not typecheck.
Here you can reuse labels, so {} ({1 = 17}, {1 = true}):{1 :int} {1 :bool} is legal,
but in some languages (e.g. OCaml) you cant.
Records reduction
Slide 159
(record2)
(record3)
72
5.3
Mutable Store
Mutable Store
Most languages have some kind of mutable store. Two main choices:
1 What weve got in L1 and L2:
e ::= ... | := e |! | x
Slide 160
variables let you refer to a previously calculated value and let you
overwrite that value with another.
void foo(x:int) {
Slide 161
implicit dereferencing,
l = l + x
...}
We are staying with option 1 here. But we will now overcome some limitations of references
in L1/L2:
can only store ints we would like to store any value
cannot create new locations (all must exist at beginning)
cannot write functions that abstract on locations fn l :intref !l
References
T
Slide 162
::= ... | := e | !
| e1 := e2 |!e | ref e |
We are now allowing variables of T ref type, e.g.fn x:int ref !x. Whole programs should
now have no locations at the start. They should create new locations with ref.
73
References Typing
(ref)
e:T
ref e : T ref
(assign)
Slide 163
(deref)
(loc)
e1 :T ref e2 :T
e1 := e2 :unit
e:T ref
!e:T
() = T ref
:T ref
References Reduction
A location is a value:
v ::= ... |
Stores s were finite partial maps from L to Z. From now on, take them to
Slide 164
(ref1)
(ref2)
(deref1)
(deref2)
Slide 165
(assign1)
(assign2)
(assign3)
h ref v , si h, s + { 7 v }i
/ dom(s)
he, si he , s i
h ref e, si h ref e , s i
h!, si hv , si
if
he, si he , s i
h!e, si h!e , s i
h := v , si hskip, s + { 7 v }i if dom(s)
he, si he , s i
h := e, si h := e , s i
he, si he , s i
he := e2 , si he := e2 , s i
A ref has to do something at runtime ( ref 0, ref 0) should return a pair of two new
locations, each containing 0, not a pair of one location repeated.
Note the typing and this dynamics permit locations to contain locations, e.g. ref( ref 3).
This semantics no longer has determinacy, for a technical reason new locations are
chosen arbitrarily. At the cost of some slight semantic complexity, we could regain
determinacy by working up to alpha for locations.
Within the language you cannot do arithmetic on locations (can in C, cant in Java)
or test whether one is bigger than another. In L3 you cannot even test locations for
equality (in ML you can).
This store just grows during computation an implementation can garbage collect.
74
We dont have an explicit deallocation operation if you do, you need a very baroque
type system to prevent dangling pointers being dereferenced.
Type-checking the store
For L1, our type properties used dom()
Slide 166
(!x) 3) end
Slide 167
he, {}i
h6, ...i
e2
Have made a recursive function by tying the knot by hand, not using let val rec . To do
this we needed to store function values. We couldnt do this in L2, so this doesnt contradict
the normalization theorem we had there.
s if dom() = dom(s) and if
dom(s), if () = T ref then s():T .
Slide 168
e:T and s
he , s i then e is closed and for some with disjoint
domain to we have , e :T and , s .
Implementation
The collected definition so far is in the notes, called L3.
Slide 169
+ T ), so you
can run programs. The Moscow ML record typing is more liberal than that
of L3, though.
75
5.4
Evaluation Contexts
We end this chapter by showing a slightly different style for defining operational semantics,
collecting together many of the context rules into a single (eval) rule that uses a definition
of a set of evaluation contexts to describe where in your program the next step of reduction
can take place. This style becomes much more convenient for large languages, though for
L1 and L2 theres not much advantage either way.
Evaluation Contexts
Define evaluation contexts
op e | v op
E ::=
;e |
e|v
let val
Slide 170
| if
x :T =
in
then
e else e |
e2 end |
( , e) | (v , ) | #1 | #2 |
inl
:T | inr :T |
case
of inl (x :T )
e | inr (x :T ) e |
he, si he , s i
hE [e], si hE [e ], s i
Slide 171
(app1), (app2), (let1), (pair1), (pair2), (proj3), (proj4), (inl), (inr), (case1),
(record1), (record3), (ref2), (deref2), (assign2), (assign3).
To (eval) we add all the computation rules (all the rest) (op + ), (op ),
(seq1), (if1), (if2), (while), (fn), (let2), (letrecfn), (proj1), (proj2), (case2),
(case3), (record2), (ref1), (deref1), (assign1).
Theorem 23 The two definitions of define the same relation.
A Little History
Slide 172
Formal logic
1880
1930s
1940s
Fortran
1950s
1960s
1970s
1981
Standard ML definition
1985
Haskell
1987
Subtyping
1980s
Module systems
1980
Object calculus
1990
1990
And now? module systems, distribution, mobility, reasoning about objects, security, typed compilation,.......
76
5.5
L3 syntax
Booleans b B = {true, false}
Integers n Z = {..., 1, 0, 1, ...}
Locations L = {l , l0 , l1 , l2 , ...}
Variables x X for a set X = {x, y, z, ...}
Labels lab LAB for a set LAB = {p, q, ...}
Operations op ::= + |
Types:
T
int | bool | unit | T1 T2 |T1 T2 |T1 + T2 |{lab 1 :T1 , .., lab k :Tk }|T ref
::=
Expressions
e
::=
n | b | e1 op e2 | if e1 then e2 else e3 |
e1 := e2 |!e | ref e | |
skip | e1 ; e2 |
while e1 do e2 |
fn x :T e | e1 e2 | x |
let val x :T = e1 in e2 end|
let val rec x :T1 T2 = (fn y:T1 e1 ) in e2 end|
(e1 , e2 ) | #1 e | #2 e|
inl e:T | inr e:T |
case e of inl (x1 :T1 ) e1 | inr (x2 :T2 ) e2 |
{lab 1 = e1 , .., lab k = ek } | #lab e
(where in each record (type or expression) no lab occurs more than once)
In expressions fn x :T e the x is a binder. In expressions let val x :T = e1 in e2 end
the x is a binder. In expressions let val rec x :T1 T2 = (fn y:T1 e1 ) in e2 end
the y binds in e1 ; the x binds in (fn y:T e1 ) and in e2 . In case e of inl (x1 :T1 ) e1 |
inr (x2 :T2 ) e2 the x1 binds in e1 and the x2 binds in e2 .
L3 semantics
Stores s are finite partial maps from L to the set of all values.
Values v ::= b | n | skip | fn x :T e|(v1 , v2 )|inl v :T | inr v :T |{lab 1 = v1 , .., lab k = vk }|
(op +)
hn1 + n2 , si hn, si
if n = n1 + n2
(op )
hn1 n2 , si hb, si
if b = (n1 n2 )
(op1)
(op2)
he1
he1 , si he1 , s i
op e2 , si he1 op e2 , s i
he2 , si he2 , s i
hv op e2 , si hv op e2 , s i
(seq1) hskip; e2 , si he2 , si
(seq2)
he1 , si he1 , s i
he1 ; e2 , si he1 ; e2 , s i
77
hif e1 then e2
he1 , si he1 , s i
else e3 , si hif e1 then e2 else e3 , s i
(while)
hwhile e1 do e2 , si hif e1 then (e2 ; while e1 do e2 ) else skip, si
(app1)
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
(app2)
he2 , si he2 , s i
hv e2 , si hv e2 , s i
he1 , si he1 , s i
end, si hlet val x :T = e1 in e2 end, s i
(let2)
hlet val x :T = v in e2 end, si h{v /x }e2 , si
(letrecfn) let val rec x :T1 T2 = (fn y:T1 e1 ) in e2 end
{(fn y:T1 let val rec x :T1 T2 = (fn y:T1 e1 ) in e1 end)/x }e2
(pair1)
he1 , si he1 , s i
h(e1 , e2 ), si h(e1 , e2 ), s i
(pair2)
he2 , si he2 , s i
h(v1 , e2 ), si h(v1 , e2 ), s i
(proj3)
(inl)
he, si he , s i
h#2 e, si h#2 e , s i
he, si he , s i
hinl e:T , si hinl e :T , s i
(case1)
he, si he , s i
78
(inr)
he, si he , s i
hinr e:T , si hinr e :T , s i
(record1)
(record3)
(ref1) h ref v , si h, s + { 7 v }i
/ dom(s)
he, si he , s i
h ref e, si h ref e , s i
(ref2)
(deref1) h!, si hv , si
he, si he , s i
h!e, si h!e , s i
(deref2)
(assign1) h := v , si hskip, s + { 7 v }i
(assign2)
he, si he , s i
h := e, si h := e , s i
(assign3)
he, si he , s i
he := e2 , si he := e2 , s i
if dom(s)
L3 Typing
Type environments, TypeEnv2, are finite partial functions from L X to Tloc T
such that
dom().() Tloc
x dom().(x ) T
(int) n:int
(bool) b:bool
(op +)
for n Z
e1 :int
e2 :int
e1 + e2 :int
(op )
e1 :int
e2 :int
e1 e2 :bool
e2 :T
e3 :T
(if) e1 :bool
if e1 then e2 else e3 :T
79
(skip) skip:unit
e2 :T
(seq) e1 :unit
e1 ; e2 :T
e2 :unit
(while) e1 :bool
while e1 do e2 :unit
(var) x :T
(fn)
if (x ) = T
, x :T e:T
fn x :T e : T T
e2 :T
(app) e1 :T T
e1 e2 :T
(let)
e1 :T
, x :T e2 :T
let val x :T = e1 in e2 end:T
(inl)
e:T1
inl e:T1 + T2 :T1 + T2
(inr)
e:T2
inr e:T1 + T2 :T1 + T2
(case)
(record)
e:T1 + T2
, x :T1 e1 :T
, y:T2 e2 :T
(recordproj)
80
(ref)
e:T
ref e : T ref
e2 :T
(assign) e1 :T ref
e1 := e2 :unit
(deref) e:T ref
!e:T
(loc)
5.6
() = T ref
:T ref
Exercises
81
Slide 173
Our type systems so far would all be annoying to use, as theyre quite rigid (Pascal-like).
There is little support for code reuse, so you would have to have different sorting code for,
e.g., int lists and int int lists.
Polymorphism
Ability to use expressions at many different types.
Slide 174
Recall
e1 :T T
(app)
Slide 175
e2 :T
e1 e2 :T
so cant type
82
Subsumption
Better? Any value of type {p:int, q:int} can be used wherever a value
of type {p:int} is expected. (*)
<: T , read as
T is a subtype of T (a T is useful in more contexts than a T ).
Introduce a subtyping relation between types, written T
Slide 176
e:T
T <: T
e:T
(sub)
Example
(var)
Slide 177
(var)
(var)
x:{p:int} x:{p:int}
{} 3:int
{} 4:int
(record-proj)
(record)
x:{p:int} #p x:int
{} {p = 3, q = 4}:{p:int, q:int}
()
(fn)
(sub)
{} (fn x:{p:int} #p x):{p:int} int
{} {p = 3, q = 4}:{p:int}
(app)
{} (fn x:{p:int} #p x){p = 3, q = 4}:int
where () is {p:int, q:int}
<: {p:int}
T <: T
T <: T
Slide 178
(s-trans)
T <: T
T <: T
T <: T
Subtyping Records
Forgetting fields on the right:
{lab 1 :T1 , .., lab k :Tk , lab k+1 :Tk+1 , .., lab k+k :Tk+k }
<:
Slide 179
(s-record-width)
T1 <: T1 .. Tk <: Tk
{lab 1 :T1 , .., lab k :Tk } <: {lab 1 :T1 , .., lab k :Tk }
(s-record-width)
{r:int} <: {}
83
(s-record-width)
(s-record-depth)
Another example:
(s-rec-w)
{p:int, q:int} <: {p:int}
(s-rec-w)
(s-rec-d)
{x:{p:int, q:int}, y:{r:int}} <: {x:{p:int, q:int}}
{x:{p:int, q:int}} <: {x:{p:int}}
(s-trans)
{x:{p:int, q:int}, y:{r:int}} <: {x:{p:int}}
(s-record-order)
Slide 180
a permutation of 1, .., k
{lab 1 :T1 , .., lab k :Tk } <: {lab (1) :T(1) , .., lab (k) :T(k) }
(the subtype order is not anti-symmetric it is a preorder, not a partial
order)
Subtyping Functions
(s-fn)
Slide 181
T1 <: T1
T2 <: T2
T1 T2 <: T1 T2
f = fn x:{p:int} {p = #p x, q = 28}
we have
Slide 182
{} f :{p:int} {p:int}
as
Slide 183
we have
84
Subtyping Products
Just like (s-record-depth)
(s-pair)
Slide 184
T1 <: T1 T2 <: T2
T1 T2 <: T1 T2
Subtyping Sums
Exercise.
Subtyping References
Are either of these any good?
Slide 185
T <: T
T ref <: T ref
T <: T
T ref <: T ref
No...
Semantics
No change (note that weve not changed the expression grammar).
Properties
Slide 186
e ::= ... | (T )e
with typing rule
e:T
(T )e:T
Slide 187
The following development is taken from [Pierce, Chapter 18], where you can find more
details (including a treatment of self and a direct semantics for a featherweight fragment
of Java).
85
let val
Slide 188
end
in
let val
Slide 189
end
in
Object Generators
let val
fn
y:unit
let val
Slide 190
end
in
86
counterClass:CounterRep Counter =
x:CounterRep
Slide 191
let val
fn
newCounter:unit Counter =
y:unit
let val
x:CounterRep = {p = ref 0} in
counterClass x
resetCounterClass:CounterRep ResetCounter =
x:CounterRep
let val
super = counterClass x in
class Counter
{ protected int p;
Counter() { this.p=0; }
int get () { return this.p; }
Slide 193
87
A
A
= {} with {p:int}
= A with {q:bool}
A = A with {r:int}
Slide 194
{}
{p:int}
Object (ish!)
{p:int, q:bool}
6.1
{p:int, r:int}
q A
qqq
q
q
q
Exercises
Exercise 31 For each of the following, either give a type derivation or explain why it is
untypable.
1. {} {p = {p = {p = {p = 3}}}}:{p:{}}
2. {} fn x:{p:bool, q:{p:int, q:bool}} #q #p x : ?
3. {} fn f:{p:int} int (f {q = 3}) + (f {p = 4}) : ?
4. {} fn f:{p:int} int (f {q = 3, p = 2}) + (f {p = 4}) : ?
Exercise 32 For each of the two bogus T ref subtype rules on Slide 185, give an example
program that is typable with that rule but gets stuck at runtime.
Exercise 33 What should the subtype rules for sums T + T be?
Exercise 34 ...and for let and let rec ?
Exercise 35 Prove a Progress Theorem for L3 with subtyping.
88
Concurrency
Slide 195
Concurrency
Our focus so far has been on semantics for sequential computation. But
the world is not sequential...
multi-processor machines
multi-threading (perhaps on a single processor)
networked machines
Problems
89
Theme: as for sequential languages, but much more so, its a complicated
world.
Aim of this lecture: just to give you a taste of how a little semantics can
be used to express some of the fine distinctions. Primarily (1) to boost
Slide 199
your intuition for informal reasoning, but also (2) this can support rigorous
proof about really hairy crypto protocols, cache-coherency protocols,
comms, database transactions,....
Going to define the simplest possible concurrent language, call it L1 , and
explore a few issues. Youve seen most of them informally in C&DS.
B = {true, false}
Z = {..., 1, 0, 1, ...}
Locations L = {l , l0 , l1 , l2 , ...}
Booleans b
Integers n
Operations
op ::= + |
Expressions
Slide 200
| e1 ; e2 |
while
e1 e2
T
e1 do e2 |
(thread)
(parallel)
Slide 201
e:unit
e:proc
e1 :proc
e2 :proc
e1 e2 :proc
(parallel1)
(parallel2)
he1 , si he1 , s i
he1 e2 , si he1 e2 , s i
he2 , si he2 , s i
he1 e2 , si he1 e2 , s i
90
Slide 203
h() l := 2, {l 7 1}i
hl := 1 l := 2, {l 7 0}i
hl := 1 (), {l 7 2}i
NB from here on, we are using () instead of skip thats the ML syntax.
But, assignments and dereferencing are atomic. For example,
hl := 3498734590879238429384 | l := 7, {l 7 0}i
will reduce to a state with l either 3498734590879238429384 or 7, not
Slide 204
something with the first word of one and the second word of the other.
Implement?
But but, in (l
interleaved.
Think of (l
91
h(l := 1+!l)
92
/ h()
(), {l 7 8}i
/ h()
(), {l 7 7}i
/ h()
(), {l 7 1}i
/ h()
(), {l 7 8}i
+
r
/
/
5
7
r
+
+
w
)
)
7
5
5
+
+
r
r
w
'
)
(l := 7+!l), {l 7 0}i
h(l := 1) (l := 7), {l 7 0}i
h(l := 1 + 0) (l := 7 + 0), {l 7 0}i
7
5
r
w
)
'
h(l := 1 + 0) (l := 7), {l 7 0}i
hl := 1 (), {l 7 7}i
h(l := 1+!l) (l := 7 + 0), {l 7 0}i
5
+
r
w
+
'
)
hl := 1 + 0 (), {l 7 7}i
h(l := 1+!l) (l := 7), {l 7 0}i
)
+
r
/
/
hl := 1+!l (), {l 7 7}i
Note that the labels +, w and r in the picture are just informal hints as to how those
transitions were derived they are not actually part of the reduction relation.
Some of the nondeterministic choices dont matter, as you can get back to the same state.
Others do...
Morals
Almost certainly you (as the programmer) didnt want all those 3
outcomes to be possible need better idioms or constructs for
programming.
So, how do we get anything coherent done?
Need some way(s) to synchronize between threads, so can enforce
mutual exclusion for shared data.
cf. Lamports Bakery algorithm from Concurrent and Distributed
Slide 206
Systems. Can you code that in L1 ? If not, whats the smallest extension
required?
Usually, though, you can depend on built-in support from the scheduler,
e.g. for mutexes and condition variables (or, at a lower level, tas or
cas).
See this in the library for a good discussion of mutexes and condition variables: A. Birrell,
J. Guttag, J. Horning, and R. Levin. Thread synchronization: a Formal Specification. In G.
Nelson, editor, System Programming with Modula-3, chapter 5, pages 119-129. PrenticeHall, 1991.
See N. Lynch. Distributed Algorithms for other mutual exclusion algorithms (and much else
besides).
Consider simple mutexes, with commands to lock an unlocked mutex and to unlock a locked
mutex (and do nothing for an unlock of an unlocked mutex).
Adding Primitive Mutexes
Mutex names m
M = {m, m1 , ...}
Slide 207
(lock)
(lock)
| lock m | unlock m
lock m:unit
(unlock)
unlock m:unit
(unlock)
Note that (lock) atomically (a) checks the mutex is currently false, (b) changes its state,
and (c) lets the thread proceed.
Also, there is no record of which thread is holding a locked mutex.
93
Need to adapt all the other semantic rules to carry the mutex state M
around. For example, replace
(op2)
Slide 208
he2 , si he2 , s i
hv op e2 , si hv op e2 , s i
by
(op2)
he2 , s, M i he2 , s , M i
hv op e2 , s, M i hv op e2 , s , M i
Using a Mutex
Consider
= m M.false, is:
= {l 7 0} and initial
lock
m
&
= M0 + {m 7 true})
In all the intervening states (until the first unlock ) the second lock cant proceed.
Look back to behaviour of the program without mutexes. Weve essentially cut down to the
top and bottom paths (and also added some extra reductions for lock , unlock , and ;).
In this example, l := 1+!l and l := 7+!l commute, so we end up in the same final state
whichever got the lock first. In general, that wont be the case.
Using Several Mutexes
lock
Slide 210
e=
Locking Disciplines
So, suppose we have several programs e1 , ..., ek , all well-typed with
Slide 211
There are many possible locking disciplines. Well focus on one, to see
how it and the properties it guarantees can be made precise and
proved.
94
Slide 212
These are semantic properties again. In general, it wont be computable whether they hold.
For simple ei , though, its often obvious. Further, one can construct syntactic disciplines
that are checkable and are sufficient to guarantee these.
Problem: Need a Thread-Local Semantics
Our existing semantics defines the behaviour only of global configurations
(parallel1)
h := n, s, M i hskip, s + { 7 n}, M i
he1 , s, M i
he1 e2 , s, M i
if
he1 , s , M i
he1 e2 , s , M i
Slide 214
he , s , M i, with rules
(t-assign1)
:=n
:= n skip
a
(t-parallel1)
e1 e1
a
e1 e2 e1 e2
:=n
(c-assign)
e e dom(s)
he, s, M i he , s + { 7 n}, M i
95
dom(s)
e if e does
lock
(l := 1 + n; unlock m)
(l := n ; unlock m)
l:=n
skip; unlock
unlock
:= 1+!l ; unlock m)
(l := 1+!l ; unlock m)
!l=n
Slide 216
skip; (l
unlock
m
for any n
for n
=1+n
skip
= s + {l 7 1 + s(l )},
he e , s, M0 i hskip e , s , M0 i
96
Global Semantics
Thread-Local Semantics
if n = n1 + n2
if b = (n1 n2 )
(op +)
hn1 + n2 , s, M i hn, s, M i
if n = n1 + n2
(t-op +)
n1 + n2 n
(op )
hn1 n2 , s, M i hb, s, M i
if b = (n1 n2 )
(t-op )
n1 n2 b
he1 , s, M i he1 , s , M i
(op1)
he1 op e2 , s, M i
he1
e1 e1
(t-op1)
op e2 , s , M i
e1 op e2 e1 op e2
he2 , s, M i he2 , s , M i
(op2)
hv op e2 , s, M i hv op
(deref)
h!, s, M i hn, s, M i
(assign1)
(seq1)
e2 e2
(t-op2)
h := n, s, M i hskip, s + { 7 n}, M i
(assign2)
e2 , s , M i
v op e2 v op e2
!=n
(t-deref)
if dom(s)
! n
:= n skip
(t-assign2)
e e
a
:= e := e
he, s, M i he , s , M i
h := e, s, M i h := e , s , M i
hskip; e2 , s, M i he2 , s, M i
skip; e2 e2
(t-seq1)
he1 , s, M i he1 , s , M i
(seq2)
he1 ; e2 , s, M i
:=n
(t-assign1)
e1 e1
(t-seq2)
he1 ; e2 , s , M i
e1 ; e2 e1 ; e2
(if1)
(t-if1)
(if2)
(t-if2)
(if3)
he1 , s, M i he1 , s , M i
hif e1 then e2 else e3 , s, M i hif
e1
then e2 else e3 , s , M i
e1 e1
(t-if3)
(while)
(t-while)
(parallel1)
(parallel2)
(lock)
he1 , s, M i he1 , s , M i
(t-parallel1)
he1 e2 , s, M i he1 e2 , s , M i
(t-parallel2)
he1 e2 , s , M i
(unlock)
e1 e2 e1 e2
a
he2 , s, M i he2 , s , M i
he1 e2 , s, M i
e1 e1
(t-lock)
e2 e2
a
e1 e2 e1 e2
lock m
lock m ()
(t-unlock)
unlock m
unlock m
()
(c-tau)
e e
he, s, M i he , s, M i
:=n
(c-assign)
e e
he, s, M i he , s + { 7 n}, M i
!=n
(c-deref)
dom(s)
e e
dom(s) s() = n
he, s, M i he , s, M i
97
lock m
(c-lock)
e e
M (m)
he, s, M i he , s, M + {m 7 true}i
unlock m
(c-unlock)
e e
he, s, M i he , s, M + {m 7 false}i
1
2
3
e
e1
e2
...
Slide 218
Slide 219
h(e1 ... ek ), s, M0 i h
e1 , s1 , M1 i h
e2 , s2 , M2 i ...
We know each e
i is a corresponding parallel composition. Look at the
points at which each ei acquires its final lock. That defines a serialization
order. In between times, consider commutativity of actions of the different
Slide 220
Slide 221
98
7.1
Exercises
Exercise 36 Are the mutexes specified here similar to those described in C&DS?
Exercise 37 Can you show all the conditions for O2PL are necessary, by giving for
each an example that satisfies all the others and either is not serialisable or deadlocks?
Exercise 38 Prove the Conjecture about it.
Exercise 39 Write a semantics for an extension of L1 with threads that are more
like Unix threads (e.g. with thread ids, fork, etc..). Include some of the various ways Unix
threads can exchange information.
99
Semantic Equivalence
Semantic Equivalence
Slide 222
2+2 4
Slide 223
2+2
esin(x) dx =
0
esin(x) dx
0
:= 0; 4) (l := 1; 3+!l )
How about (l
They will produce the same result (in any store), but you cannot replace
one by the other in an arbitrary program context. For example:
C [ ] = +!l
Slide 224
C [l := 0; 4] =
(l := 0; 4)+!l
6
Slide 225
e1 ; (e2 ; e3 ) (e1 ; e2 ); e3
Slide 226
let val
100
op ::= ... |=
e1 :T ref
Slide 227
(op =)
(op =)
e2 :T ref
e1 = e2 :bool
h = , si hb, si
if b
= ( = )
y = ref 0 in
end end
Slide 228
y = ref 0 in
end end
f g
The last two examples are taken from A.M. Pitts, Operational Semantics and Program
Equivalence. In: G. Barthe, P. Dybjer and J. Saraiva (Eds), Applied Semantics. Lecture
Notes in Computer Science, Tutorial, Volume 2395 (Springer-Verlag, 2002), pages 378-412.
http://www.cl.cam.ac.uk/~amp12/papers/opespe/opespe-lncs.pdf
With a good notion of semantic equivalence, we might:
1. understand what a program is
2. prove that some particular expression (say an efficient algorithm) is
equivalent to another (say a clear specification)
Slide 229
3. prove the soundness of general laws for equational reasoning about
programs
4. prove some compiler optimizations are sound (source/IL/TAL)
5. understand the differences between languages
101
4.
e1 e2 e3 = e1 e3
must be a congruence
if e1
5.
e1 e2 e2 e1 ,
Define e1
Slide 231
(a)
he2 , si hv , s i.
hv , s i and
In this definition, part (b), we require that e1 and e2 result in the same value and moreover
the same store. This is because, if we were to equate two programs e1 and e2 that result
in different stores say s1 (l )6= s2 (l ) then we could distinguish them using the following
contexts, and the semantic equivalence would not be a congruence.
Slide 232
If T
= unit then C = ; !l .
If T
= bool then C = if
If T
= int then C = l1 := ; !l .
then !l else !l .
op e2 | e1 op
C ::=
if
if
then
e2 else e3 | if e1 then
e1 then e2 else
:= |
Slide 233
; e2 | e1 ; |
while
do
C [e1 ] T C [e2 ].
e3 |
e2 | while e1 do
Say T
has the congruence property if whenever e1
else
T e2 we have,
102
Slide 234
he, si h
e1 , s1 i ...
Using e
Consider the possible reduction sequences of a state h := e, si. Recall that (by
examining the reduction rules), if h := e, si he1 , s1 i then either that is an
instance of (assign1), with n.e = n dom(s) e1 = skip s = s + { 7 n},
or it is an instance of (assign2), with e1 .he, si h
e1 , s1 i e1 = ( := e1 ). We
know also that hskip, si does not reduce.
Now (using Determinacy), for any e and s we have either
Case: h := e, si , i.e.
h := e, si he1 , s1 i he2 , s2 i ...
hence all these must be instances of (assign2), with
he, si h
e1 , s1 i h
e2 , s2 i ...
and e1 = ( := e1 ), e2 = ( := e2 ),...
Case: (h := e, si ), i.e.
h := e, si he1 , s1 i he2 , s2 i... hek , sk i 6
hence all these must be instances of (assign2) except the last, which must be
an instance of (assign1), with
he, si h
e1 , s1 i h
e2 , s2 i ... h
ek1 , sk1 i
and e1 = ( := e1 ), e2 = ( := e2 ),..., ek1 = ( := ek1 ) and for some n we
have ek1 = n, ek = skip, and sk = sk1 + { 7 n}.
(the other possibility, of zero or more (assign1) reductions ending in a stuck
state, is excluded by Theorems 2 and 3 (type preservation and progress))
Now, if h := e, si , by the above there is an infinite reduction sequence for
he, si, so by e T e there is an infinite reduction sequence of he , si, so (using
(assign2)) there is an infinite reduction sequence of h := e , si.
103
1.
Slide 235
hv , s i and
he2 , si hv , s i.
So:
2 + 2 int
4 for any
(l := 0; 4) 6int
(l := 1; 3+!l ) for any
Slide 236
1. a list of tokens
if then else
!l
skip
skip
l :=
0
Slide 237
Slide 238
Suppose
When is e1 ; e2
unit
e2 ; e1 ?
A sufficient condition: they dont mention any locations (but not necessary... e.g. if e1 does
but e2 doesnt)
104
8.1
Contextual equivalence
The definition of semantic equivalence works fine for L1. However, when we come to L2 and
L3, the simple notion does not give a congruence.
Here is a basic definition of an equivalence for L3.
Contextual equivalence for L3
e1 :T and e2 :T .
We say that they are contextually equivalent if, for every context C such
that {} C [e1 ]:unit and {} C [e2 ]:unit, we have either
Slide 239
(a)
hskip, s1 i and
8.2
Exercises
Exercise 40 Prove some of the other cases of the Congruence theorem for semantic
equivalence in L1.
Exercise 41 Prove that if 1 e1 :unit and 2 e2 :unit in L1, and 1 is disjoint from
2 , then e1 ; e2 unit
e2 ; e1 where = 1 2
Exercise 42 Prove that the programs l :int ref l := 0:unit and l :int ref l := 1:unit,
considered as L3 programs, are not contextually equivalent. Hint: find a context that will
diverge for one of them, but not for the other.
Epilogue
Epilogue
Slide 240
Lecture Feedback
Slide 241
Please do fill in the lecture feedback form we need to know how the
course could be improved / what should stay the same.
Good language design?
Need:
Slide 242
105
Slide 243
106
107
Here is an interpreter and type checker for L1. You can download the source code from the
course website.
(* 2002-11-08 -- Time-stamp: <2006-10-25 09:22:33 pes20>
(* Peter Sewell
-*-SML-*- *)
*)
(* *********************)
(* the abstract syntax *)
(* *********************)
type loc = string
datatype oper = Plus | GTEQ
datatype expr =
Integer of int
| Boolean of bool
| Op of expr * oper * expr
| If of expr * expr * expr
| Assign of loc * expr
| Deref of loc
| Skip
| Seq of expr * expr
| While of expr * expr
(* **********************************)
(* an interpreter for the semantics *)
(* **********************************)
fun
|
|
|
is_value
is_value
is_value
is_value
(Integer n) = true
(Boolean b) = true
(Skip) = true
_ = false
108
109
(* **********************************)
(* typing
*)
(* **********************************)
(* types *)
datatype type_L1 =
int
| unit
| bool
datatype type_loc =
intref
type typeEnv = (loc*type_loc) list
(* in the semantics, type environments gamma are partial functions
from locations to the singleton set {intref}. Here, just as we did for
stores, we represent them as a list of loc*type_loc pairs containing,
for each l in the domain of the type environment, exactly one element
of the form (l,intref). *)
(* ****************)
(* type inference *)
(* ****************)
110
111
Here is an interpreter and type checker for L1, written in Java by Matthew Parkinson.
Note the different code organization between the ML and Java versions: the ML has a
datatype with a constructor for each clause of the abstract syntax grammar, and reduce
and infertype function definitions that each have a case for each of those constructors; the
Java has a subclass of Expression for each clause of the abstract syntax, each of which
defines smallStep and typecheck methods.
public class L1 {
public static void main(String
Location l1 = new Location
Location l2 = new Location
Location l3 = new Location
State s1 = new State()
.add(l1,new Int(1))
.add(l2,new Int(5))
.add(l3,new Int(0));
[] args) {
("l1");
("l2");
("l3");
112
Location(String n) {
this.name = n;
}
public String toString() {return name;}
}
class State {
java.util.HashMap store = new java.util.HashMap();
//Used for setting the initial store for testing not used by
//semantics of L1
State add(Location l, Value v) {
store.put(l,v);
return this;
}
void update(Location l, Value v) throws CanNotReduce {
if(store.containsKey(l)) {
if(v instanceof Int) {
store.put(l,v);
}
else throw new CanNotReduce("Can only store integers");
}
else throw new CanNotReduce("Unknown location!");
}
Value lookup(Location l) throws CanNotReduce {
if(store.containsKey(l)) {
return (Int)store.get(l);
}
else throw new CanNotReduce("Unknown location!");
}
public String toString() {
String ret = "[";
java.util.Iterator iter = store.entrySet().iterator();
while(iter.hasNext()) {
java.util.Map.Entry e = (java.util.Map.Entry)iter.next();
ret += "(" + e.getKey() + " |-> " + e.getValue() + ")";
if(iter.hasNext()) ret +=", ";
}
return ret + "]";
}
}
class Environment {
java.util.HashSet env = new java.util.HashSet();
//Used to initially setup environment, not used by type checker.
Environment add(Location l) {
env.add(l); return this;
}
boolean contains(Location l) {
return env.contains(l);
}
}
class Type {
int type;
Type(int t) {type =
public static final
public static final
public static final
t;}
Type BOOL = new Type(1);
Type INT = new Type(2);
Type UNIT = new Type(3);
113
114
115
116
117
How to do Proofs
The purpose of this handout is give a general guide as to how to prove theorems. This
should give you some help in answering questions that begin with Show that the following
is true . . . . It is based on notes by Myra VanInwegen, with additional text added by Peter
Sewell in C.1. Many thanks to Myra for making her original notes available.
The focus here is on doing informal but rigorous proofs. These are rather different from
the formal proofs, in Natural Deduction or Sequent Calculus, that were introduced in the
Logic and Proof course. Formal proofs are derivations in one of those proof systems they
are in a completely well-defined form, but are often far too verbose to deal with by hand
(although they can be machine-checked). Informal proofs, on the other hand, are the usual
mathematical notion of proof: written arguments to persuade the reader that you could, if
pushed, write a fully formal proof.
This is important for two reasons. Most obviously, you should learn how to do these proofs.
More subtly, but more importantly, only by working with the mathematical definitions in
some way can you develop a good intuition for what they mean trying to do some proofs
is the best way of understanding the definitions.
C.1
How to go about it
Proofs differ, but for many of those you meet the following steps should be helpful.
1. Make sure the statement of the conjecture is precisely defined. In particular, make
sure you understand any strange notation, and find the definitions of all the auxiliary
gadgets involved (e.g. definitions of any typing or reduction relations mentioned in the
statement, or any other predicates or functions).
2. Try to understand at an intuitive level what the conjecture is saying verbalize out
loud the basic point. For example, for a Type Preservation conjecture, the basic
point might be something like if a well-typed configuration reduces, the result is still
well-typed (with the same type).
3. Try to understand intuitively why it is true (or false...). Identify what the most
interesting cases might be the cases that you think are most likely to be suspicious,
or hard to prove. Sometimes its good to start with the easy cases (if the setting
is unfamiliar to you); sometimes its good to start with the hard cases (to find any
interesting problems as soon as possible).
4. Think of a good basic strategy. This might be:
(a) simple logic manipulations;
(b) collecting together earlier results, again by simple logic; or
(c) some kind of induction.
5. Try it! (remembering you might have to backtrack if you discover you picked a strategy
that doesnt work well for this conjecture). This might involve any of the following:
(a) Expanding definitions, inlining them. Sometimes you can just blindly expand all
definitions, but more often its important to expand only the definitions which
you want to work with the internal structure of otherwise things just get too
verbose.
(b) Making abbreviations defining a new variable to stand for some complex gadget
youre working with, saying e.g.
118
by ...
by ...
as ...
Here the e might be any mathematical object arithmetic expressions, or expressions of some grammar, or formulae. Some handy equations over formulae
are given in C.2.2.
(d) Proving a formula based on its structure. For example, to prove a formula x
S.P (x) you would often assume you have an arbitrary x and then try to prove
P (x).
Take an arbitrary x S.
We now have to show P (x):
This is covered in detail in C.2.3. Much proof is of this form, automatically
driven by the structure of the formula.
(e) Using an assumption youve made above.
(f) Induction. As covered in the 1B Semantics notes, there are various kinds of induction you might want to use: mathematical induction over the natural numbers,
structural induction over the elements of some grammar, or rule induction over
the rules defining some relation (especially a reduction or typing relation). For
each, you should:
i. Decide (and state!) what kind of induction youre using. This may need
some thought and experience, and you might have to backtrack.
ii. Remind yourself what the induction principle is exactly.
iii. Decide on the induction hypothesis youre going to use, writing down a predicate which is such that the conclusion of the induction principle implies
the thing youre trying to prove. Again, this might need some thought. Take
care with the quantifiers here its suspicious if your definition of has
any globally-free variables...
iv. Go through each of the premises of the induction principle and prove each one
(using any of these techniques as appropriate). Many of those premises will
be implications, e.g. x N.(x) (x + 1), for which you can do a proof
based on the structure of the formula taking an arbitrary x, assuming
(x), and trying to prove (x + 1). Usually at some point in the latter youd
make use of the assumption (x).
6. In all of the above, remember: the point of doing a proof on paper is to use the
formalism to help you think to help you cover all cases, precisely and also to
communicate with the reader. For both, you need to write clearly:
(a) Use enough words! Assume, We have to show, By such-and-such we know,
Hence,...
(b) Dont use random squiggles. Its good to have formulae properly nested within
text, with and no or between lines of text.
7. If it hasnt worked yet... either
(a) youve make some local mistake, e.g. mis-instantiated something, or used the
same variable for two different things, or not noticed that you have a definition
you should have expanded or an assumption you should have used. Fix it and
continue.
119
(b) youve discovered that the conjecture is really false. Usually at this point its
a good idea to construct a counterexample that is as simple as possible, and to
check carefully that it really is a counterexample.
(c) you need to try a different strategy often, to use a different induction principle
or to strengthen your induction hypothesis.
(d) you didnt really understand intuitively what the conjecture is saying, or what
the definitions it uses mean. Go back to them again.
8. If it has worked: read through it, skeptically, and check. Maybe youll need to re-write
it to make it comprehensible: proof discovery is not the same as proof exposition. See
the example proofs in the Semantics notes.
9. Finally, give it to someone else, as skeptical and careful as you can find, to see if they
believe it to see if they believe that what youve written down is a proof, not that
they believe that the conjecture is true.
C.2
First, Ill explain informal proof intuitively, giving a couple of examples. Then Ill explain
how this intuition is reflected in the sequent rules from Logic and Proof.
In the following, Ill call any logic statement a formula. In general, what well be trying to
do is prove a formula, using a collection of formulas that we know to be true or are assuming
to be true. Theres a big difference between using a formula and proving a formula. In fact,
what you do is in many ways opposite. So, Ill start by explaining how to prove a formula.
C.2.1
Here are the logical connectives and a very brief decription of what each means.
P Q
P Q
P
P Q
P Q
x S.P (x)
x S.P (x)
C.2.2
Equivalences
These are formulas that mean the same thing, and this is indicated by a between them.
The fact that they are equivalent to each other is justified by the truth tables of the connectives.
120
definition of
definition of
definition of
de Morgans Laws
extension to quantifiers
distributive laws
coalescing quantifiers
these ones apply if
x is not free in Q
C.2.3
P Q
P Q
P
(P Q)
(P Q)
(x.P (x))
(x.P (x))
P (Q R)
P (Q R)
(x.P (x)) (x.Q(x))
(x.P (x)) (x.Q(x))
(x.P (x)) Q
(x.P (x)) Q
(x.P (x)) Q
(x.P (x)) Q
P Q
(P Q) (Q P )
P false
P Q
P Q
x.P (x)
x.P (x)
(P Q) (P R)
(P Q) (P R)
x.(P (x) Q(x))
x.(P (x) Q(x))
(x.P (x) Q)
(x.P (x) Q)
(x.P (x) Q)
(x.P (x) Q)
For each of the logical connectives, Ill explain how to handle them.
x S.P (x) This means For all x in S, P is true of x. Such a formula is called a
universally quantified formula. The goal is to prove that the property P , which has some
xs somewhere in it, is true no matter what value in S x takes on. Often the S is left
out. For example, in a discussion of lists, you might be asked to prove l.length l > 0
x. member(x, l). Obviously, l is a list, even if it isnt explicitly stated as such.
There are several choices as to how to prove a formula beginning with x. The standard
thing to do is to just prove P (x), not assuming anything about x. Thus, in doing the proof
you sort of just mentally strip off the x. What you would write when doing this is Let x be
any S. However, there are some subtletiesif youre already using an x for something else,
you cant use the same x, because then you would be assuming something about x, namely
that it equals the x youre already using. In this case, you need to use alpha-conversion1 to
change the formula you want to prove to y S.P (y), where y is some variable youre not
already using, and then prove P (y). What you could write in this case is Since x is already
in use, well prove the property of y.
An alternative is induction, if S is a set that is defined with a structural definition. Many
objects youre likely to be proving properties of are defined with a structural definition.
This includes natural numbers, lists, trees, and terms of a computer language. Sometimes
you can use induction over the natural numbers to prove things about other objects, such
as graphs, by inducting over the number of nodes (or edges) in a graph.
You use induction when you see that during the course of the proof you would need to use
the property P for the subparts of x in order to prove it for x. This usually ends up being
the case if P involves functions defined recursively (i.e., the return value for the function
depends on the function value on the subparts of the argument).
A special case of induction is case analysis. Its basically induction where you dont use the
inductive hypothesis: you just prove the property for each possible form that x could have.
Case analysis can be used to prove the theorem about lists above.
A final possibility (which you can use for all formulas, not just for universally quantified
ones) is to assume the contrary, and then derive a contradiction.
1 Alpha-equivalence says that the name of a bound variable doesnt matter, so you can change it at will
(this is called alpha-conversion). Youll get to know the exact meaning of this soon enough so I wont explain
this here.
121
x S.P (x) This says There exists an x in S such that P holds of x. Such a formula is
called an existentially quantified formula. The main way to prove this is to figure out what
x has to be (that is, to find a concrete representation of it), and then prove that P holds of
that value. Sometimes you cant give a completely specified value, since the value you pick
for x has to depend on the values of other things you have floating around. For example,
say you want to prove
x, y R.x < y sin x < 0 sin y > 0 z.x < z z < y sin z = 0
where R is the set of real numbers. By the time you get to dealing with the z.x < z z <
y sin z = 0, you will have already assumed that x and y were any real numbers. Thus the
value you choose for z has to depend on whatever x and y are.
An alternative way to prove x S.P (x) is, of course, to assume that no such x exists, and
derive a contradiction.
To summarize what Ive gone over so far: to prove a universally quantified formula, you must
prove it for a generic variable, one that you havent used before. To prove an existentially
quantified formula, you get to choose a value that you want to prove the property of.
P Q This says If P is true, then Q is true. Such a formula is called an implication,
and it is often pronounced P implies Q. The part before the sign (here P ) is called
the antecedent, and the part after the sign (here Q) is called the consequent. P Q is
equivalent to P Q, and so if P is false, or if Q is true, then P Q is true.
The standard way to prove this is to assume P , then use it to help you prove Q. Note that
I said that you will be using P . Thus you will need to follow the rules in Section C.2.4 to
deal with the logical connectives in P .
Other ways to prove P Q involve the fact that it is equivalent to P Q. Thus, you can
prove P without bothering with Q, or you can just prove Q without bothering with P .
To reason by contradiction you assume that P is true and that Q is not true, and derive a
contradiction.
Another alternative is to prove the contrapositive: Q P , which is equivalent to it.
P Q This says P is true if and only if Q is true. The phrase if and only if is usually
abbreviated iff. Basically, this means that P and Q are either both true, or both false.
Iff is usually used in two main ways: one is where the equivalence is due to one formula
being a definition of another. For example, A B (x. x A x B) is the standard
definition of subset. For these iff statements, you dont have to prove them. The other use
of iff is to state the equivalence of two different things. For example, you could define an
SML function fact:
fun fact 0 = 1
| fact n = n * fact (n - 1)
Since in SML whole numbers are integers (both positive and negative) you may be asked
to prove: fact x terminates x 0. The standard way to do this is us the equivalence
P Q is equivalent to P Q Q P . And so youd prove that (fact x terminates
x 0) (x 0 fact x terminates).
P This says P is not true. It is equivalent to P false, thus this is one of the ways
you prove it: you assume that P is true, and derive a contradiction (that is, you prove
false). Heres an example of this, which youll run into later this year: the undecidability
of the halting problem can be rephrased as x RM. x solves the halting problem, where
RM is the set of register machines. The proof of this in your Computation Theory notes
follows exactly the pattern I describedit assumes there is such a machine and derives a
contradiction.
122
The other major way to prove P is to figure out what the negation of P is, using equivalences like De Morgans Law, and then prove that. For example, to prove x N . y
N . x = y 2 , where N is the set of natural numbers, you could push in the negation to get:
x N . y N . x 6= y 2 , and then you could prove that.
P Q This says P is true and Q is true. Such a formula is called a conjunction. To
prove this, you have to prove P , and you have to prove Q.
P Q This says P is true or Q is true. This is inclusive or: if P and Q are both true,
then P Q is still true. Such a formula is called a disjunction. To prove this, you can prove
P or you can prove Q. You have to choose which one to prove. For example, if you need to
prove (5 mod 2 = 0) (5 mod 2 = 1), then youll choose the second one and prove that.
However, as with existentials, the choice of which one to prove will often depend on the
values of other things, like universally quantified variables. For example, when you are
studying the theory of programming languages (you will get a bit of this in Semantics), you
might be asked to prove
P ML.
P is properly typed
(the evaluation of P runs forever) (P evaluates to a value)
where ML is the set of all ML programs. You dont know in advance which of these will be
the case, since some programs do run forever, and some do evaluate to a value. Generally,
the best way to prove the disjunction in this case (when you dont know in advance which
will hold) is to use the equivalence with implication. For example, you can use the fact
that P Q is equivalent to P Q, then assume P , then use this to prove Q. For
example, your best bet to proving this programming languages theorem is to assume that
the evaluation of P doesnt run forever, and use this to prove that P evaluates to a value.
C.2.4
You often end up using a formula to prove other formulas. You can use a formula if someone
has already proved that its true, or you are assuming it because it was in an implication,
namely, the A in A B. For each logical connective, Ill tell you how to use it.
x S.P (x) This formula says that something is true of all elements of S. Thus, when
you use it, you can pick any value at all to use instead of x (call it v), and then you can use
P (v).
x S.P (x) This formula says that there is some x that satisfies P . However, you do not
know what it is, so you can not assume anything about it. The usual approach it to just
say that the thing that is being said to exist is just x, and use the fact that P holds of x to
prove something else. However, if youre already using an x for something else, you have to
pick another variable to represent the thing that exists.
To summarize this: to use a universally quantified formula, you can choose any value, and
use that the formula holds for that variable. To use an existentially quantified formula, you
must not assume anything about the value that is said to exists, so you just use a variable
(one that you havent used before) to represent it. Note that this is more or less opposite
of what you do when you prove a universally or existentially quantified formula.
P Usually, the main use of this formula is to prove the negation of something else.
An example is the use of reduction to prove the unsolvability of various problems in the
Computation Theory (youll learn all about this in Lent term). You want to prove Q,
where Q states that a certain problem (Problem 1) is decidable (in other words, you want
to prove that Problem 1 is not decidable). You know P , where P states that another
problem (Problem 2) is decidable (i.e. P says that Problem 2 is not decidable). What you
123
do basically is this. You first prove Q P , which says that if Problem 1 is decidable, then
so is Problem 2. Since Q P P Q, you have now proved P Q. You already
know P , so you use modus ponens2 to get that Q.
P Q The main way to use this is that you prove P , and then you use modus ponens to
get Q, which you can then use.
P Q The main use of this is to replace an occurrence of P in a formula with Q, and
vise versa.
P Q Here you can use both P and Q. Note, youre not required to use both of them, but
they are both true and are waiting to be used by you if you need them.
P Q Here, you know that one of P or Q is true, but you do not know which one. To use
this to prove something else, you have to do a split: first you prove the thing using P , then
you prove it using Q.
Note that in each of the above, there is again a difference in the way you use a formula,
verses the way you prove it. They are in a way almost opposites. For example, in proving
P Q, you have to prove both P and Q, but when you are using the formula, you dont
have to use both of them.
C.3
An Example
There are several exercises in the Semantics notes that ask you to prove something. Here,
well go back to Regular Languages and Finite Automata. (If theyve faded, its time
to remind yourself of them.) The Pumping Lemma for regular sets (PL for short) is an
astonishingly good example of the use of quantifiers. Well go over the proof and use of the
PL, paying special attention to the logic of whats happening.
C.3.1
Proving the PL
My favorite book on regular languages, finite automata, and their friends is the Hopcroft
and Ullman book Introduction to Automata Theory, Languages, and Computation. You
should locate this book in your college library, and if it isnt there, insist that your DoS
order it for you.
In the Automata Theory book, the Pumping Lemma is stated as: Let L be a regular set.
Then there is a constant n such that if z is any word in L, and |z| n, we may write z = uvw
in such a way that |uv| n, |v| 1, and for all i 0, uv i w is in L. The Pumping Lemma
is, in my experience, one of the most difficult things about learning automata theory. It
is difficult because people dont know what to do with all those logical connectives. Lets
write it as a logical formula.
L RegularLanguages.
n. z L. |z| n
u v w. z = uvw |uv| n |v| 1
i 0. uv i w L
Complicated, eh? Well, lets prove it, using the facts that Hopcroft and Ullman have
established in the chapters previous to the one wih the PL. Ill give the proof and put in
square brackets comments about what Im doing.
Let L be any regular language. [Here Im dealing with the L RegularLanguages by
stating that Im not assuming anything about L.] Let M be a minimal-state deterministic
2 Modus
124
finite state machine accepting L. [Here Im using a fact that Hopcroft and Ullman have
already proved about the equivalence of regular languages and finite automata.] Let n be
the number of states in this finite state machine. [Im dealing with the n by giving a very
specific value of what it will be, based on the arbitrary L.] Let z be any word in L. [Thus
I deal with z L.] Assume that |z| n. [Thus Im taking care of the by assuming the
antecedent.]
Say z is written a1 a2 . . . am , where m n. Consider the states that M is in during the
processing of the first n symbols of z, a1 a2 . . . an . There are n + 1 of these states. Since
there are only n states in M , there must be a duplicate. Say that after symbols aj and ak
we are in the same state, state s (i.e. theres a loop from this state that the machine goes
through as it accepts z), and say that j < k. Now, let u = a1 a2 . . . aj . This represents the
part of the string that gets you to state s the first time. Let v = aj+1 . . . ak . This represents
the loop that takes you from s and back to it again. Let w = ak+1 . . . am , the rest of word
z. [We have chosen definite values for u, v, and w.] Then clearly z = uvw, since u, v, and
w are just different sections of z. |uv| n since u and v occur within the first n symbols
of z. |v| 1 since j < k. [Note that were dealing with the formulas connected with by
proving each of them.]
Now, let i be a natural number (i.e. 0). [This deals with i 0.] Then uv i w L. [Finally
our conclusion, but we have to explain why this is true.] This is because we can repeat the
loop from s to s (represented by v) as many times as we like, and the resulting word will
still be accepted by M .
C.3.2
Using the PL
Now we use the PL to prove that a language is not regular. This is a rewording of Example
2
3.1 from Hopcroft and Ullman. Ill show that L = {0i |i is an integer, i 1} is not regular.
Note that L consists of all strings of 0s whose length is a perfect square. I will use the PL.
I want to prove that L is not regular. Ill assume the negation (i.e., that L is regular) and
derive a contradiction. So here we go. Remember that what Im emphasizing here is not
the finite automata stuff itself, but how to use a complicated theorem to prove something
else.
Assume L is regular. We will use the PL to get a contradiction. Since L is regular, the PL
applies to it. [We note that were using the part of the PL for this particular L.] Let n
be as described in the PL. [This takes care of using the n. Note that we are not assuming
2
anything about its actual value, just that its a natural number.] Let z = 0n . [Since the PL
says that something is true of all zs, we can choose the one we want to use it for.] So by the
PL there exist u, v, and w such that z = uvw, |uv| n, |v| 1. [Note that we dont assume
anything about what the u, v, and w actually are; the only thing we know about them is
what the PL tells us about them. This is where people trying to use the PL usually screw
up.] The PL then says that for any i, then uv i w L. Well, then uv 2 w L. [This is using
the i 0 bit.] However, n2 < |uv 2 w| n2 + n, since 1 |v| n. But n2 + n < (n + 1)2 .
Thus |uv 2 w| lies properly between n2 and (n + 1)2 and is thus not a perfect square. Thus
uv 2 w is not in L. This is a contradiction. Thus our assumption (that L was regular) was
incorrect. Thus L is not a regular language.
C.4
In this section, I will show how the intuitive approach to things that Ive described above
is reflected in the sequent calculus rules. A sequent is , where and are sets of
125
(1)
For each logic connective,4 Ill give the rules for it, and explain how it relates to the intuitive
way of using or proving formulas. For each connective there are at least two rules for it: one
for the left side of the , and one for the right side. This corresponds to having different
ways to treat a formula depending on whether youre using it (for formulas on the left hand
side of the ) or proving it (for formulas on the right side of the ).
Its easiest to understand these rules from the bottom up. The conclusion of the rule (the
sequent below the horizontal line) is what we want to prove. The hypotheses of the rule
(the sequents above the horizontal line) are how we go about proving it. Well have to use
more rules, adding to the top, to build up the proof of the hypothesis, but this at least tells
us how to get going.
You can stop when the formula you have on the top is a basic sequent. This is where
theres at least one formula (say P ) thats in both and . You can see why this is the
basic true formula: it says that if P and the other formulas in are true, then P or one of
the other formula in is true.
In building proofs from these rules, there are several ways that you end up with formulas
to the left of the , where you can use them rather than proving them. One is that youve
already proved it before. This is shown with the cut rule:
, P P,
(cut)
The , P in the first sequent in the hypotheses means that to the right of the we have
the set consisting of the formula P plus all the formulas in , i.e., if all formulas in are
true, then P or one of the formulas in is true. Similarly P, to the left of the in the
second sequent means the set consisting of the formula P plus all the formulas in .
We read this rule from the bottom up to make sense of it. Say we want to prove one of the
formulas in from the formulas in , and we want to make use of a formula P that weve
already proved. The fact that weve proved P is shown by the left hypothesis (of course,
unless the left hypothesis is itself a basic sequent, then in a completed proof there will be
more lines on top of the left hypothesis, showing the actual proof of the sequent). The fact
that we are allowed to use P in the proof of is shown in the right hand hypothesis. We
continue to build the proof up from there, using P .
Some other ways of getting formulas to the left of the are shown in the rules (r) and
( r) below.
x S.P (x) The two rules for universally quantified formulas are:
P (v),
(l)
x.P (x),
, P (x)
(r)
, x.P (x)
3 In your Logic and Proof notes, the symbol that divides from is . However, that conflicts with the
use of as implication. Thus I will use . You will see something similar in Semantics, where it separates
assumptions (of the types of variables) from something that they allow you to prove.
4 I wont mention iff here: as P Q is equivalent to P Q Q P , we dont need separate rules for
it.
126
, P (v)
(r)
, x.P (x)
, P
(l)
P,
Lets start with the right rule first. I said that the way to prove P is to assume P and
derive a contradiction. If is the empty set, then this is exactly what this rule says: If
there are no formulas to the right hand side of the , then this means that the formulas in
are inconsistent (that means, they cannot all be true at the same time). This means that
you have derived a contradiction. So if is the empty set, the hypothesis of the rule says
that, assuming P , you have obtained a contradiction. Thus, if you are absolutely certain
about all your other hypotheses, then you can be sure that P is not true. The best way to
understand the rule if is not empty is to write out the meaning of the sequents in terms
of the meaning of the sequent given by Equation 1 and work out the equivalence of the top
and bottom of the rule using the equivalences in your Logic and Proof notes.
The easiest way to understand (l) is again by using equivalences.
127
P, , Q
( r)
, P Q
The rule ( l) easily understood using the intuitive explanation of how to use P Q given
above. First, we have to prove P . This is the left hypothesis. Then we can use Q, which is
what the right hypothesis says.
The right rule ( r) is also easily understood. In order to prove P Q, we assume P ,
then use this to prove Q. This is exactly what the hypothesis says.
P Q The rules for conjunction are:
P, Q,
(l)
P Q,
, P , Q
(r)
, P Q
Both of these rules are easily explained by the intuition above. The left rule (l) says that
when you use P Q, you can use P and Q. The right rule says that to prove P Q you must
prove P , and you must prove Q. You may wonder why we need separate hypotheses for
the two different proofs. We cant just put P, Q to the right of the in a single hypothesis,
because that would mean that were proving one of the other of them (see the meaning of
the sequent given in Equation 1). So we need separate hypotheses to make sure that each
of P and Q has actually been proved.
P Q The rules for disjunction are:
P, Q,
(l)
P Q,
, P, Q
(r)
, P Q
These are also easily understood by the intuitive explanations above. The left rule says that
to prove something (namely, one of the formulas in ) using P Q, you need to prove it
using P , then prove it using Q. The right rule says that in order to prove P Q, you can
prove one or the other. The hypothesis says that you can prove one or the other, because
in order to show a sequent true, you only need to show that one of the formulas in
is true.
128