Semantics of Programming Languages: Computer Science Tripos, Part 1B 2011
Semantics of Programming Languages: Computer Science Tripos, Part 1B 2011
and
or
implies
not
x.(x) for all
x.(x) exists
a A element of
a
1
, ..., a
n
the set with elements a
1
, ..., a
n
A
1
A
2
union
A
1
A
2
intersection
A
1
A
2
subset or equal
A
1
A
2
cartesian product (set of pairs)
Finite partial functions
a
1
b
1
, ..., a
n
b
n
nite partial function mapping each a
i
to b
i
dom(s) set of elements in the domain of s
f +a b the nite partial function f extended or overridden with
a maps to b
, x:T the nite partial function extended with x T
only used where x not in dom()
,
, s
, s
, s
) big-step evaluation
s store s is well-typed with respect to type environment
T <: T
e e
Semantic
Equivalence
10
Concurrency
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.
Slide 11
The Big Picture
Discrete
Maths
RLFA
Logic
& Proof
ML
/
Java and
C&DS
.
Computability
..
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Compiler
Construction
and Optimising
Compilers
Semantics
.
Foundations of
Functional
Programming
Types
Topics in
Concurrency
Spec&Ver I,II
Denotational
Semantics
Advanced
Programming
Languages?
Slide 12
Admin
Please let me know of typos, and if it is too fast/too slow/too
interesting/too dull (please complete the on-line feedback at the end)
Not all previous Tripos questions are relevant (see the notes)
Exercises in the notes.
Implementations on web.
Books (Hennessy, Pierce, Winskel)
11
2 A First Imperative Language
Slide 13
L1
Slide 14
L1 Example
L1 is an imperative language with store locations (holding integers),
conditionals, and while loops. For example, consider the program
l
2
:= 0;
while !l
1
1 do (
l
2
:=!l
2
+!l
1
;
l
1
:=!l
1
+1)
in the initial store l
1
3, l
2
0.
Slide 15
L1 Syntax
Booleans b B = true, false
Integers n Z = ..., 1, 0, 1, ...
Locations L = l , l
0
, l
1
, l
2
, ...
Operations op ::=+ [
Expressions
e ::= n [ b [ e
1
op e
2
[ if e
1
then e
2
else e
3
[
:= e [ ! [
skip [ e
1
; e
2
[
while e
1
do e
2
Write L
1
for the set of all expressions.
Points to note:
well return later to exactly what the set L
1
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 seman-
tics to dene various forms of automata:
Slide 16
Transition systems
A transition system consists of
a set Cong, and
a binary relation Cong Cong.
The elements of Cong are often called congurations or states. The
relation is called the transition or reduction relation. We write
inx, so c c
.
To compare with the automata you saw in Regular Languages and Finite Automata: a
transition system is like an NFA
.
, is a unary predicate (a subset of Cong) dened by c , i c
.c c
.
The particular transition systems we use for L1 are as follows.
Slide 17
L1 Semantics (1 of 4) Congurations
Say stores s are nite partial functions from L to Z. For example:
l
1
7, l
3
23
Take congurations to be pairs e, s) of an expression e and a store s, so
our transition relation will have the form
e, s) e
, s
)
Denition. A nite partial function f from a set A to a set B is a set containing a nite
number n 0 of pairs (a
1
, b
1
), ..., (a
n
, b
n
), often written a
1
b
1
, ..., a
n
b
n
, for which
i 1, .., n.a
i
A (the domain is a subset ofA)
i 1, .., n.b
i
B (the range is a subset of B)
i 1, .., n, j 1, .., n.i ,= j a
i
,= a
j
(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) = l
1
, l
3
and ran(s) = 7, 23. Note that a nite partial function can be empty, just .
We write store for the set of all stores.
13
Slide 18
Transitions are single computation steps. For example we will have:
l := 2 + !l , l 3)
l := 2 + 3, l 3)
l := 5, l 3)
skip, l 5)
,
want to keep on until we get to a value v, an expression in
V = B Z skip.
Say e, s) is stuck if e is not a value and e, s) ,. For example
2 + true will be stuck.
We could dene the values in a dierent, but equivalent, style: Say values v are expressions
from the grammar v ::=b [ n [ skip.
Now dene the behaviour for each construct of L1 by giving some rules that (together)
dene a transition relation .
Slide 19
L1 Semantics (2 of 4) Rules (basic operations)
(op +) n
1
+ n
2
, s) n, s) if n = n
1
+ n
2
(op ) n
1
n
2
, s) b, s) if b = (n
1
n
2
)
(op1)
e
1
, s) e
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op2)
e
2
, s) e
2
, s
)
v op e
2
, s) v op e
2
, s
)
How to read these? The rule (op +) says that for any instantiation of the metavariables
n, n
1
and n
2
(i.e. any choice of three integers), that satises the sidecondition, there is a
transition from the instantiated conguration 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 e
1
, e
1
, e
2
, s, s
1
= 5, e
2
= 6 + 7, op = +, s = , s
= ,
and using (op +) with n
1
= 2, n
2
= 3, s = . Note couldnt begin with (op2) as e
1
= 2 + 3
is not a value, and couldnt use (op +) directly on (2 + 3) + (6 + 7) as 2 + 3 and 6 + 7 are
not numbers from Z just expressions which might eventually evaluate to numbers (recall,
by convention the n in the rules ranges over Z only).
Second transition: using (op2) with e
1
= 5, e
2
= 6 + 7, e
2
= 13, op = +, s = , s
= ,
and using (op +) with n
1
= 6, n
2
= 7, s = . Note that to use (op2) we needed that e
1
= 5
is a value. We couldnt use (op1) as e
1
= 5 does not have any transitions itself.
Third transition: using (op +) with n
1
= 5, n
2
= 13, s = .
To nd each transition we do something like proof search in natural deduction: starting
with a state (at the bottom left), look for a rule and an instantiation of the metavariables
in that rule that makes the left-hand-side of its conclusion match that state. Beware that
in general there might be more than one rule and one instantiation that does this. If there
isnt a derivation concluding in e, s) e
, s
, s
)
:= e, s) := e
, s
)
(seq1) skip; e
2
, s) e
2
, s)
(seq2)
e
1
, s) e
1
, s
)
e
1
; e
2
, s) e
1
; e
2
, s
)
15
Slide 22
Example
l := 3; !l , l 0) skip; !l , l 3)
!l , l 3)
3, l 3)
l := 3; l := !l , l 0) ?
15 + !l , ) ?
Slide 23
L1 Semantics (4 of 4) The rest (conditionals and while)
(if1) if true then e
2
else e
3
, s) e
2
, s)
(if2) if false then e
2
else e
3
, s) e
3
, s)
(if3)
e
1
, s) e
1
, s
)
if e
1
then e
2
else e
3
, s) if e
1
then e
2
else e
3
, s
)
(while)
while e
1
do e
2
, s) if e
1
then (e
2
; while e
1
do e
2
) else skip, s)
Slide 24
Example
If
e = (l
2
:= 0; while !l
1
1 do (l
2
:=!l
2
+!l
1
; l
1
:=!l
1
+1))
s = l
1
3, l
2
0
then
e, s)
?
That concludes our denition of L1. The full denition is collected on page 28.
Slide 25
Determinacy
Theorem 1 (L1 Determinacy) If e, s) e
1
, s
1
) and
e, s) e
2
, s
2
) then e
1
, s
1
) = e
2
, s
2
).
Proof see later
Note that top-level universal quantiers are usually left out the theorem really says For
all e, s, e
1
, s
1
, e
2
, s
2
, if e, s) e
1
, s
1
) and e, s) e
2
, s
2
) then e
1
, s
1
) = e
2
, s
2
).
16
Slide 26
L1 implementation
Many possible implementation strategies, including:
1. animate the rules use unication to try to match rule conclusion
left-hand-sides against a conguration; use backtracking search to nd
all possible transitions. Hand-coded, or in Prolog/LambdaProlog/Twelf.
2. write an interpreter working directly over the syntax of congurations.
Coming up, in ML and Java.
3. compile to a stack-based virtual machine, and an interpreter for that.
See Compiler Construction.
4. compile to assembly language, dealing with register allocation etc. etc.
See Compiler Construction/Optimizing Compilers.
Slide 27
L1 implementation
Will implement an interpreter for L1, following the denition. Use mosml
(Moscow ML) as the implementation language, as datatypes and pattern
matching are good for this kind of thing.
First, must pick representations for locations, stores, and expressions:
type loc = string
type store = (loc
*
int) list
Weve chosen to represent locations as strings, so they pretty-print trivially. A lower-level
implementation would use ML references.
In the semantics, a store is a nite partial function from locations to integers. In the
implementation, we represent a store as a list of loc*int pairs containing, for each in the
domain of the store and mapped to n, exactly one element of the form (l,n). The order of
the list will not be important. This is not a very ecient implementation, but it is simple.
Slide 28
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
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 overow exception if + overows.
17
Slide 29
Store operations
Dene auxiliary operations
lookup : store
*
loc -> int option
update : store
*
(loc
*
int) -> store option
which both return NONE if given a location that is not in the domain of the
store. Recall that a value of type T option is either NONE or
SOME v for a value v of T.
Slide 30
The single-step function
Now dene the single-step function
reduce : expr
*
store -> (expr
*
store) option
which takes a conguration (e,s) and returns either
NONE, if e, s) ,,
or SOME (e,s), if it has a transition e, s) e
, s
).
Note that if the semantics didnt dene a deterministic transition system
wed have to be more elaborate.
(you might think it would be better ML style to use exceptions instead of these options;
that would be ne).
Slide 31
(op +), (op )
fun reduce (Integer n,s) = NONE
| reduce (Boolean b,s) = NONE
| reduce (Op (e1,opr,e2),s) =
(case (e1,opr,e2) of
(Integer n1, Plus, Integer n2) =>
SOME(Integer (n1+n2), s)
| (Integer n1, GTEQ, Integer n2) =>
SOME(Boolean (n1 >= n2), s)
| (e1,opr,e2) =>
...
Contrast this code with the semantic rules given earlier.
Slide 32
(op1), (op2)
...
if (is value e1) then
case reduce (e2,s) of
SOME (e2,s) =>
SOME (Op(e1,opr,e2),s)
| NONE => NONE
else
case reduce (e1,s) of
SOME (e1,s) =>
SOME(Op(e1,opr,e2),s)
| NONE => NONE )
18
Note that the code depends on global properties of the semantics, including the fact that it
denes a deterministic transition system, so the comments indicating that particular lines
of code implement particular semantic rules are not the whole story.
Slide 33
(assign1), (assign2)
| reduce (Assign (l,e),s) =
(case e of
Integer n =>
(case update (s,(l,n)) of
SOME s => SOME(Skip, s)
| NONE => NONE)
| =>
(case reduce (e,s) of
SOME (e,s) =>
SOME(Assign (l,e), s)
| NONE => NONE ) )
Slide 34
The many-step evaluation function
Now dene the many-step evaluation function
evaluate: expr
*
store -> (expr
*
store) option
which takes a conguration (e,s) and returns the (e,s) such that
e, s)
, s
2
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op2b)
e
1
, s) e
1
, s
)
e
1
op v, s) e
1
op v, s
)
In this language (call it L1b)
(l := 1; 0) + (l := 2; 0), l 0)
5
0, l 1 )
Left-to-right evaluation is arguably more intuitive than right-to-left.
One could also underspecify, taking both (op1) and (op1b) rules. That language doesnt
have the Determinacy property.
Slide 37
Language design 2. Assignment results
Recall
(assign1) := n, s) skip, s + n) if dom(s)
(seq1) skip; e
2
, s) e
2
, s)
So
l := 1; l := 2, l 0) skip; l := 2, l 1)
skip, l 2)
Weve chosen := n to result in skip, and e
1
; e
2
to only progress if
e
1
= skip, not for any value. Instead could have this:
(assign1) := n, s) n, s + ( n)) if dom(s)
(seq1) v; e
2
, s) e
2
, s)
Matter of taste? Another possiblity: return the old value, e.g. in ANSI C signal handler
installation.
Slide 38
Language design 3. Store initialization
Recall that
(deref) !, s) n, s) if dom(s) and s() = n
(assign1) := n, s) skip, s + n) if dom(s)
both require dom(s), otherwise the expressions are stuck.
Instead, could
1. implicitly initialize all locations to 0, or
2. allow assignment to an / dom(s) to initialize that .
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?)
Slide 39
Language design 4. Storable values
Recall stores s are nite partial functions from L to Z, with rules:
(deref) !, s) n, s) if dom(s) and s() = n
(assign1) := n, s) skip, s + n) if dom(s)
(assign2)
e, s) e
, s
)
:= e, s) := e
, s
)
Can store only integers. l := true, s) is stuck.
Why not allow storage of any value? of locations? of programs?
Also, store is global. We will consider programs that can create new
locations later.
Slide 40
Language design 5. Operators and basic values
Booleans are really not integers (unlike in C)
The L1 impl and semantics arent quite in step.
Exercise: x the implementation to match the semantics.
Exercise: x the semantics to match the implementation.
Slide 41
Expressiveness
Is L1 expressive enough to write interesting programs?
yes: its Turing-powerful (try coding an arbitrary register machine in
L1).
no: theres no support for gadgets like functions, objects, lists, trees,
modules,.....
Is L1 too expressive? (ie, can we write too many programs in it)
yes: wed like to forbid programs like 3 + false as early as possible,
rather than let the program get stuck or give a runtime error. Well do
so with a type system.
2.2 Typing
Slide 42
L1 Typing
21
Slide 43
Type systems
used for
describing when programs make sense
preventing certain kinds of errors
structuring programs
guiding language design
Ideally, well-typed programs dont get stuck.
Type systems are also used to provide information to compiler optimizers; to enforce security
properties, from simple absence of buer overows to sophisticated information-ow 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.
Slide 44
Formal type systems
We will dene a ternary relation e:T, read as expression e has type
T, under assumptions on the types of locations that may occur in e.
For example (according to the denition coming up):
if true then 2 else 3 + 4 : int
l
1
:intref if !l
1
3 then !l
1
else 3 : int
, 3 + false : T for any T
, if true then 3 else false : int
Note that the last is excluded despite the fact that when you execute the program you will
always get an int type systems dene 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.
Slide 45
Types for L1
Types of expressions:
T ::= int [ bool [ unit
Types of locations:
T
loc
::= intref
Write T and T
loc
for the sets of all terms of these grammars.
Let range over TypeEnv, the nite partial functions from locations L
to T
loc
. Notation: write a as l
1
:intref, ..., l
k
:intref instead of
l
1
intref, ..., l
k
intref.
concretely, T = int, bool, unit and T
loc
= intref.
in this language, there is only one type in T
loc
, so a can be thought of as just a set
of locations. (Later, T
loc
will be more interesting.)
22
Slide 46
Dening the type judgement e:T (1 of 3)
(int) n:int for n Z
(bool) b:bool for b true, false
(op +)
e
1
:int
e
2
:int
e
1
+ e
2
:int
(op )
e
1
:int
e
2
:int
e
1
e
2
:bool
(if)
e
1
:bool e
2
:T e
3
:T
if e
1
then e
2
else e
3
: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 +)
e
1
:int
e
2
:int
e
1
+ e
2
:int
but this is merely visual layout. Derivations using such a rule should be written as if it was
in the horizontal form.
(op +)
e
1
:int e
2
:int
e
1
+ e
2
:int
Slide 47
Example
To show if false then 2 else 3 + 4:int we can give a type
derivation like this:
(if)
(bool)
false:bool
(int)
2:int
if false then 2 else 3 + 4:int
where is
(op +)
(int)
3:int
(int)
4:int
3 + 4:int
Slide 48
Dening the type judgement e:T (2 of 3)
(assign)
() = intref e:int
:= e:unit
(deref)
() = intref
!:int
Here the () = intref just means dom().
23
Slide 49
Dening the type judgement e:T (3 of 3)
(skip) skip:unit
(seq)
e
1
:unit e
2
:T
e
1
; e
2
:T
(while)
e
1
:bool e
2
:unit
while e
1
do e
2
: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.
Slide 50
Properties
Theorem 2 (Progress) If e:T and dom() dom(s) then either e
is a value or there exist e
, s
such that e, s) e
, s
).
Theorem 3 (Type Preservation) If e:T and dom() dom(s)
and e, s) e
, s
) then e
).
From these two we have that well-typed programs dont get stuck:
Theorem 4 (Safety) If e:T, dom() dom(s), and
e, s)
, s
) then either e
, s
such
that e
, s
) e
, s
).
(well discuss how to prove these results soon)
Semantic style: one could make an explicit denition of what congurations are runtime
errors. Here, instead, those congurations are just stuck.
Slide 51
Type checking, typeability, and type inference
Type checking problem for a type system: given , e, T, is e:T
derivable?
Type inference problem: given and e, nd T such that e:T is
derivable, or show there is none.
Second problem is usually harder than the rst. 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.
Slide 52
More Properties
Theorem 5 (Type inference) Given , e, one can nd T such that
e:T, or show that there is none.
Theorem 6 (Decidability of type checking) Given , e, T, one can
decide e:T.
Also:
Theorem 7 (Uniqueness of typing) If e:T and e:T
then
T = T
.
24
The le l1.ml contains also an implementation of a type inference algorithm for L1 take
a look.
Slide 53
Type inference Implementation
First must pick representations for types and for s:
datatype type L1 =
int
| unit
| bool
datatype type loc =
intref
type typeEnv = (loc
*
type loc) list
Now dene the type inference function
infertype : typeEnv -> expr -> type L1 option
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).
Slide 54
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)
| infertype gamma (If (e1,e2,e3))
= (case (infertype gamma e1, infertype gamma e2, infertype gamma e3) of
(SOME bool, SOME t2, SOME t3) =>
if t2=t3 then SOME t2 else NONE
| => NONE)
| infertype gamma (Deref l)
= (case lookup (gamma,l) of
SOME intref => SOME int
| NONE => NONE)
| infertype gamma (Assign (l,e))
= (case (lookup (gamma,l), infertype gamma e) of
(SOME intref,SOME int) => SOME unit
| => NONE)
| infertype gamma (Skip) = SOME unit
| infertype gamma (Seq (e1,e2))
= (case (infertype gamma e1, infertype gamma e2) of
(SOME unit, SOME t2) => SOME t2
| => NONE )
| infertype gamma (While (e1,e2))
= (case (infertype gamma e1, infertype gamma e2) of
(SOME bool, SOME unit) => SOME unit )
25
Slide 55
The Type Inference Algorithm If
...
| infertype gamma (If (e1,e2,e3))
= (case (infertype gamma e1,
infertype gamma e2,
infertype gamma e3) of
(SOME bool, SOME t2, SOME t3) =>
if t2=t3 then SOME t2 else NONE
| => NONE)
(if)
e
1
:bool
e
2
:T
e
3
:T
if e
1
then e
2
else e
3
:T
Slide 56
The Type Inference Algorithm Deref
...
| infertype gamma (Deref l)
= (case lookup (gamma,l) of
SOME intref => SOME int
| NONE => NONE)
...
(deref)
() = 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.
Slide 57
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
.. .
and lk = ref nk
in
e
end;
where s is the store l
1
n
1
, ..., l
k
n
k
and all locations that occur
in e are contained in l
1
, ..., l
k
.
(watch out for 1 and -1)
26
Slide 58
Why Not Types?
I cant write the code I want in this type system.
(the Pascal complaint) usually false for a modern typed language
Its too tiresome to get the types right throughout development.
(the untyped-scripting-language complaint)
Type annotations are too verbose.
type inference means you only have to write them where its useful
Type error messages are incomprehensible.
hmm. Sadly, sometimes true.
I really cant write the code I want.
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 inte-
gers). 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 L1: Collected denition
Syntax
Booleans b B = true, false
Integers n Z = ..., 1, 0, 1, ...
Locations L = l , l
0
, l
1
, l
2
, ...
Operations op ::=+ [
Expressions
e ::= n [ b [ e
1
op e
2
[ if e
1
then e
2
else e
3
[
:= e [ ! [
skip [ e
1
; e
2
[
while e
1
do e
2
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 nite partial functions from L to Z. Say values v are expressions from the
grammar v ::=b [ n [ skip.
(op +) n
1
+ n
2
, s) n, s) if n = n
1
+ n
2
(op ) n
1
n
2
, s) b, s) if b = (n
1
n
2
)
(op1)
e
1
, s) e
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op2)
e
2
, s) e
2
, s
)
v op e
2
, s) v op e
2
, s
)
(deref) !, s) n, s) if dom(s) and s() = n
(assign1) := n, s) skip, s + n) if dom(s)
(assign2)
e, s) e
, s
)
:= e, s) := e
, s
)
(seq1) skip; e
2
, s) e
2
, s)
(seq2)
e
1
, s) e
1
, s
)
e
1
; e
2
, s) e
1
; e
2
, s
)
(if1) if true then e
2
else e
3
, s) e
2
, s)
(if2) if false then e
2
else e
3
, s) e
3
, s)
(if3)
e
1
, s) e
1
, s
)
if e
1
then e
2
else e
3
, s) if e
1
then e
2
else e
3
, s
)
(while)
while e
1
do e
2
, s) if e
1
then (e
2
; while e
1
do e
2
) else skip, s)
28
Typing
Types of expressions:
T ::= int [ bool [ unit
Types of locations:
T
loc
::= intref
Write T and T
loc
for the sets of all terms of these grammars.
Let range over TypeEnv, the nite partial functions from locations L to T
loc
.
(int) n:int for n Z
(bool) b:bool for b true, false
(op +)
e
1
:int
e
2
:int
e
1
+ e
2
:int
(op )
e
1
:int
e
2
:int
e
1
e
2
:bool
(if)
e
1
:bool e
2
:T e
3
:T
if e
1
then e
2
else e
3
:T
(assign)
() = intref e:int
:= e:unit
(deref)
() = intref
!:int
(skip) skip:unit
(seq)
e
1
:unit e
2
:T
e
1
; e
2
:T
(while)
e
1
:bool e
2
:unit
while e
1
do e
2
:unit
29
2.4 Exercises
Exercise 1 Write a program to compute the factorial of the integer initially in location
l
1
. Take care to ensure that your program really is an expression in L1.
Exercise 2 Give full derivations of all the reduction steps of
(l
0
:= 7); (l
1
:= (!l
0
+ 2)), l
0
0, l
1
0)
Exercise 3 Give full derivations of the rst four reduction steps of the e, s) of the rst
L1 example on Slide 14.
Exercise 4 Adapt the implementation code to correspond to the two rules (op1b) and
(op2b) on Slide 36. 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 37. 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 (l
0
:= 7); (l
1
:= (!l
0
+2)) with = l
0
:intref, l
1
:intref.
Exercise 8 Give a type derivation for e on Slide 24 with = l
1
:intref, l
2
:intref, l
3
:intref .
Exercise 9 Does Type Preservation hold for the variant language with rules (assign1)
and (seq1)? on Slide 37? 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
3 Induction
Key concepts in this chapter:
Structural induction
Rule induction
Slide 59
Induction
Slide 60
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
language features, and for debugging denitions it helps you examine all
the various cases.
Most of our denitions are inductive. To prove things about them, we need
the corresponding induction principles.
Slide 61
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
structural induction.
Prove facts about all elements of a relation dened 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.
Slide 62
Principle of Mathematical Induction
For any property (x) of natural numbers x N = 0, 1, 2, ..., to
prove
x N.(x)
its enough to prove
(0) and x N.(x) (x + 1).
i.e.
x N.(x)
(NB, the natural numbers include 0)
31
Slide 63
x N.(x)
For example, to prove
Theorem 8 1 + 2 + ... + x = 1/2 x (x + 1)
use mathematical induction for
(x) = (1 + 2 + ... + x = 1/2 x (x + 1))
Theres a model proof in the notes, as an example of good style. Writing a
clear proof structure like this becomes essential when things get more
complex you have to use the formalism to help you get things right.
Emulate it!
Theorem 8 1 + 2 +... + x = 1/2 x (x + 1) .
I have annotated the proof to say whats going on.
Proof We prove x.(x), where
(state explicitly)
(x)
def
= (1 + 2 +... + x = 1/2 x (x + 1))
by mathematical induction
(state the induction principle youre using)
.
(Now show each conjunct of the premise of the induction principle)
Base case: (conjunct (0) )
(0) is
(instantiate )
(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 to
show the conclusion).
We have to show (k + 1), i.e. (state what we have to show explicitly)
(1 + 2 +... + (k + 1)) = 1/2 (k + 1) ((k + 1) + 1)
Now, the left hand side is
(1 + 2 +... + (k + 1)) = (1 + 2 +... + k) + (k + 1) (rearranging)
= (1/2 k (k + 1)) + (k + 1) (using (k) )
(say where you use the induction hypothesis assumption (k) made above)
and the right hand side is (rearranging)
1/2 (k + 1) ((k + 1) + 1) = 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)
which is equal to the LHS.
32
3.1 Abstract Syntax and Structural Induction
Slide 64
Abstract Syntax and Structural Induction
How to prove facts about all expressions, e.g. Determinacy for L1?
Theorem 1 (Determinacy) If e, s) e
1
, s
1
) and
e, s) e
2
, s
2
) then e
1
, s
1
) = e
2
, s
2
) .
First, dont forget the elided universal quantiers.
Theorem 1 (Determinacy) For all e, s, e
1
, s
1
, e
2
, s
2
, if
e, s) e
1
, s
1
) and e, s) e
2
, s
2
) then e
1
, s
1
) = e
2
, s
2
) .
Slide 65
Abstract Syntax
Then, have to pay attention to what an expression is.
Recall we said:
e ::= n [ b [ e op e [ if e then e else e [
:= e [ ! [
skip [ e; e [
while e do e
dening a set of expressions.
Slide 66
Q: Is an expression, e.g. if !l 0 then skip else (skip; l := 0):
1. a list of characters [i, f, , !, l, ..];
2. a list of tokens [ IF, DEREF, LOC "l", GTEQ, ..]; or
3. an abstract syntax tree?
if then else
skip ;
!l
0
skip
l :=
0
Slide 67
A: an abstract syntax tree. Hence: 2 + 2 ,= 4
+
2
4
1 + 2 + 3 ambiguous
(1 + 2) + 3 ,= 1 + (2 + 3)
+
+
+
1
Parentheses are only used for disambiguation they are not part of the
grammar. 1 + 2 = (1 + 2) = ((1 + 2)) = (((((1)))) + ((2)))
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 nite trees, with ordered branches,
labelled as follows:
leaves (nullary nodes) labelled by B Z (! L) skip = true, false, skip
..., 1, 0, 1, ... !l , !l
1
, !l
2
, ....
unary nodes labelled by l :=, l
1
:=, l
2
:=, ...
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 inx notation, but really
mean trees.
Slide 68
Principle of Structural Induction (for abstract syntax)
For any property (e) of expressions e, to prove
e L
1
.(e)
its enough to prove for each tree constructor c (taking k 0 arguments)
that if holds for the subtrees e
1
, .., e
k
then holds for the tree
c(e
1
, .., e
k
). i.e.
c. e
1
, .., e
k
.((e
1
) ... (e
k
)) (c(e
1
, .., e
k
))
e.(e)
where the tree constructors (or node labels) c are n, true, false, !l , skip,
l :=, while do , if then else , etc.
Slide 69
In particular, for L1: to show e L
1
.(e) its enough to show:
nullary: (skip)
b true, false.(b)
n Z.(n)
L.(!)
unary: L. e.(e) ( := e)
binary: op . e
1
, e
2
.((e
1
) (e
2
)) (e
1
op e
2
)
e
1
, e
2
.((e
1
) (e
2
)) (e
1
; e
2
)
e
1
, e
2
.((e
1
) (e
2
)) (while e
1
do e
2
)
ternary: e
1
, e
2
, e
3
.((e
1
) (e
2
) (e
3
)) (if e
1
then e
2
else e
3
)
(See how this comes directly from the grammar)
Slide 70
Proving Determinacy (Outline)
Theorem 1 (Determinacy) If e, s) e
1
, s
1
) and
e, s) e
2
, s
2
) then e
1
, s
1
) = e
2
, s
2
) .
Take
(e)
def
= s, e
, s
, e
, s
.
(e, s) e
, s
) e, s) e
, s
))
e
, s
) = e
, s
)
and show e L
1
.(e) by structural induction.
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
Slide 71
(e)
def
= s, e
, s
, e
, s
.
(e, s e
, s
e, s e
, s
)
e
, s
= e
, s
nullary: (skip)
b {true, false}.(b)
n Z.(n)
L.(!)
unary: L. e.(e) ( := e)
binary: op . e1, e2.((e1) (e2)) (e1 op e2)
e1, e2.((e1) (e2)) (e1; e2)
e1, e2.((e1) (e2)) (while e1 do e2)
ternary: e1, e2, e3.((e1) (e2) (e3)) (if e1 then e2 else e3)
We will come back later to look at some of these details.
3.2 Inductive Denitions and Rule Induction
Slide 72
Inductive Denitions and Rule Induction
How to prove facts about all elements of the L1 typing relation or the L1
reduction relation, e.g. Progress or Type Preservation?
Theorem 2 (Progress) If e:T and dom() dom(s) then either e
is a value or there exist e
, s
such that e, s) e
, s
).
Theorem 3 (Type Preservation) If e:T and dom() dom(s)
and e, s) e
, s
) then e
).
What does e, s) e
, s
) really mean?
Slide 73
Inductive Denitions
We dened the transition relation e, s) e
, s
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op +)
e
1
:int e
2
:int
e
1
+ e
2
:int
What did we actually mean?
35
Slide 74
These relations are just normal set-theoretic relations, written in inx
notation.
For the transition relation:
Start with A = L
1
store L
1
store.
Write A inx, e.g. e, s) e
, s
) instead of
(e, s, e
, s
) .
For the typing relation:
Start with A = TypeEnv L
1
types.
Write A mixx, e.g. e:T instead of (, e, T) .
Slide 75
For each rule we can construct the set of all concrete rule instances,
taking all values of the metavariables that satisfy the side condition. For
example, for (op + ) and (op1) we take all values of n
1
, n
2
, s, n
(satisfying n = n
1
+ n
2
) and of e
1
, e
2
, s, e
1
, s
.
(op+ )
2 + 2, {} 4, {} ,
(op + )
2 + 3, {} 5, {} , ...
(op1)
2 + 2, {} 4, {}
(2 + 2) + 3, {} 4 + 3, {} ,
(op1)
2 + 2, {} false, {}
(2 + 2) + 3, {} false + 3, {}
Note the last has a premise that is not itself derivable, but nonetheless this is a legitimate
instance of (op1).
Slide 76
Now a derivation of a transition e, s) e
, s
) or typing judgment
e:T is a nite tree such that each step is a concrete rule instance.
2 + 2, ) 4, )
(op+)
(2 + 2) + 3, ) 4 + 3, )
(op1)
(2 + 2) + 3 5, ) 4 + 3 5, )
(op1)
!l :int
(deref)
2:int
(int)
(!l + 2):int
(op +)
3:int
(int)
(!l + 2) + 3:int
(op +)
and e, s) e
, s
, s
such that e, s) e
, s
).
Proof Take
(, e, T)
def
= s. dom() dom(s)
value(e) ( e
, s
.e, s) e
, s
))
We show that for all , e, T, if e:T then (, e, T), by rule
induction on the denition of .
Slide 80
Principle of Rule Induction (variant form): to prove (a) for all a in the
set S
R
, its enough to prove that for each concrete rule instance
h
1
.. h
k
c
if (h
1
) ... (h
k
) h
1
S
R
.. h
k
S
R
then (c).
Instantiating to the L1 typing rules, have to show:
(int) , n.(, n, int)
(deref) , .() = intref (, !, int)
(op +) , e1, e2.((, e1, int) (, e2, int) e1:int e2:int)
(, e1 + e2, int)
(seq) , e1, e2, T.((, e1, unit) (, e2, T) e1:unit e2:T)
(, e1; e2, T)
etc.
Slide 81
Having proved those 10 things, consider an example
(!l + 2) + 3:int. To see why (, (!l + 2) + 3, int) holds:
!l :int
(deref)
2:int
(int)
(!l + 2):int
(op +)
3:int
(int)
(!l + 2) + 3:int
(op +)
37
Slide 82
Which Induction Principle to Use?
Which of these induction principles to use is a matter of convenience
you want to use an induction principle that matches the denitions youre
working with.
For completeness, observe the following:
Mathematical induction over N is essentially the same as structural induction over n ::=zero [
succ (n).
Instead of using structural induction (for an arbitrary grammar), you could use mathematical
induction on the size of terms.
Instead of using structural induction, you could use rule induction: supposing some xed
set of tree node labels (e.g. all the character strings), take A to be the set of all trees with
those labels, and consider each clause of your grammar (e.g.e ::=... [ e + e) to be a rule
e e
e + e
3.3 Example proofs
Slide 83
Example Proofs
In the notes there are detailed example proofs for Determinacy (structural
induction), Progress (rule induction on type derivations), and Type
Preservation (rule induction on reduction derivations).
You should read them off-line, and do the exercises.
Slide 84
When is a proof a proof?
Whats a proof?
Formal: a derivation in formal logic (e.g. a big natural deduction proof
tree). Often far too verbose to deal with by hand (but can
machine-check such things).
Informal but rigorous: an argument to persuade the reader that, if
pushed, you could write a fully formal proof (the usual mathematical
notion, e.g. those we just did). Have to learn by practice to see when
they are rigorous.
Bogus: neither of the above.
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 rst, perhaps) to see if they (a) can understand what youve said,
and (b) if they believe it.
Slide 85
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
2. sometimes the obvious facts are false...
3. sometimes the obvious facts are not obvious at all
4. sometimes a proof contains or suggests an algorithm that you need
eg, proofs that type inference is decidable (for fancier type systems)
38
Theorem 1 (Determinacy) If e, s) e
1
, s
1
) and e, s) e
2
, s
2
) then e
1
, s
1
) =
e
2
, s
2
) .
Proof Take
(e)
def
= s, e
, s
, e
, s
.(e, s) e
, s
) e, s) e
, s
)) e
, s
) = e
, s
)
We show e L
1
.(e) by structural induction.
Cases skip, b, n. For e of these forms there are no rules with a conclusion of the form
e, ...) .., ..) so the left hand side of the implication cannot hold, so the
implication is true.
Case !. Take arbitrary s, e
, s
, e
, s
such that !, s) e
, s
)!, s) e
, s
).
The only rule which could be applicable is (deref), in which case, for those tran-
sitions to be instances of the rule we must have
dom(s) dom(s)
e
= s() e
= s()
s
= 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 := e, s) e
, s
) := e, s)
e
, s
).
Its handy to have this lemma:
Lemma 9 For all e L
1
, if e is a value then s. e
, s
.e, s)
e
, s
).
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 e, s) e
, s
, s
= skip and s
= s + n.
case := n, s) e
, s
= skip and s
= s + n so
e
, s
) = e
, s
) as required.
case := e, s) e
, s
, s
, s
1
we have
e, s) e
1
, s
) (*) and e
= ( := e
1
).
case := e, s) e
, s
, s
1
we have e, s) e
1
, s
)(**) and e
= ( := e
1
). Now, by the
induction hypothesis (e), (*) and (**) we have e
1
, s
) = e
1
, s
), so
e
, s
) = := e
1
, s
) = := e
1
, s
) = e
, s
) as required.
Case e
1
op e
2
. Suppose (e
1
) and (e
2
).
Take arbitrary s, e
, s
, e
, s
such that e
1
op e
2
, s) e
, s
)e
1
op e
2
, s)
e
, s
).
39
By examining the expressions in the left-hand-sides of the conclusions of the rules,
and using the lemma above, the only possibilities are those below (you should
check why this is so for yourself).
case op = + and e
1
+ e
2
, s) e
, s
, s
) is an instance of (op+ ).
Then for some n
1
, n
2
we have e
1
= n
1
, e
2
= n
2
, e
= n
3
= e
for n
3
= n
1
+n
2
,
and s
= s = s
.
case op = and e
1
e
2
, s) e
, s
, s
) is an instance of (op).
Then for some n
1
, n
2
we have e
1
= n
1
, e
2
= n
2
, e
= b = e
for b = (n
1
n
2
),
and s
= s = s
.
case e
1
op e
2
, s) e
, s
, s
) is an instance of (op1).
Then for some e
1
and e
1
we have e
1
, s) e
1
, s
) (*), e
1
, s) e
1
, s
)
(**), e
= e
1
op e
2
, and e
= e
1
op e
2
. Now, by the induction hypothesis
(e
1
), (*) and (**) we have e
1
, s
) = e
1
, s
), so e
, s
) = e
1
op e
2
, s
) =
e
1
op e
2
, s
) = e
, s
) as required.
case e
1
op e
2
, s) e
, s
, s
) is an instance of (op2).
Similar, save that we use the induction hypothesis (e
2
).
Case e
1
; e
2
. Suppose (e
1
) and (e
2
).
Take arbitrary s, e
, s
, e
, s
such that e
1
; e
2
, s) e
, s
) e
1
; e
2
, s)
e
, s
).
By examining the expressions in the left-hand-sides of the conclusions of the rules,
and using the lemma above, the only possibilities are those below.
case e
1
= skip and both transitions are instances of (seq1).
Then e
, s
) = e
2
, s) = e
, s
).
case e
1
is not a value and both transitions are instances of (seq2). Then for some
e
1
and e
1
we have e
1
, s) e
1
, s
) (*), e
1
, s) e
1
, s
) (**), e
= e
1
; e
2
,
and e
= e
1
; e
2
Then by the induction hypothesis (e
1
) we have e
1
, s
) = e
1
, s
), so
e
, s
) = e
1
; e
2
, s
) = e
1
; e
2
, s
) = e
, s
) as required.
Case while e
1
do e
2
. Suppose (e
1
) and (e
2
).
Take arbitrary s, e
, s
, e
, s
, s
)
while e
1
do e
2
, s) e
, s
).
By examining the expressions in the left-hand-sides of the conclusions of the rules
both must be instances of (while), so e
, s
) = if e
1
then (e
2
; while e
1
do e
2
) else skip, s) =
e
, s
).
Case if e
1
then e
2
else e
3
. Suppose (e
1
), (e
2
) and (e
3
).
Take arbitrary s, e
, s
, e
, s
such that if e
1
then e
2
else e
3
, s) e
, s
)
if e
1
then e
2
else e
3
, s) e
, s
).
By examining the expressions in the left-hand-sides of the conclusions of the rules,
and using the lemma above, the only possibilities are those below.
case e
1
= true and both transitions are instances of (if1).
case e
1
= false and both transitions are instances of (if2).
40
case e
1
is not a value and both transitions are instances of (if3).
The rst two cases are immediate; the last uses (e
1
).
, s
.e, s) e
, s
).
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 e, s) e
, s
, s
such that e, s) e
, s
).
Proof Take
(, e, T)
def
= s.dom() dom(s) value(e) ( e
, s
.e, s) e
, s
))
We show that for all , e, T, if e:T then (, e, T), by rule induction on the
denition of .
Case (int). Recall the rule scheme
(int) n:int for n Z
It has no premises, so we have to show that for all instances , e, T of the con-
clusion 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).
We have to show value(e) ( e
, s
.e, s) e
, s
, s
.e
1
+ e
2
, s) e
, s
)).
Now the rst disjunct is false (e
1
+ e
2
is not a value), so we have to show the
second, i.e.e
, s
).e
1
+ e
2
, s) e
, s
).
By (*) one of the following holds.
case e
1
, s
.e
1
, s) e
1
, s
).
Then by (op1) we have e
1
+ e
2
, s) e
1
+ e
2
, s
), so we are done.
case e
1
is a value. By (**) one of the following holds.
case e
2
, s
.e
2
, s) e
2
, s
).
Then by (op2) e
1
+ e
2
, s) e
1
+ e
2
, s
), so we are done.
case e
2
is a value.
(Now want to use (op+ ), but need to know that e
1
and e
2
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
Case (bool),(skip). In instances of these rules the conclusion is a
value but the type is not int, so ok.
Case otherwise. In instances of all other rules the conclusion is
not a value, so ok.
(a rather trivial use of rule induction we never needed to use the
induction hypothesis, just to do case analysis of the last rule that
might have been used in a derivation of e:T).
Using the Lemma, (***) and (****) there exist n
1
Z and n
2
Z
such that e
1
= n
1
and e
2
= n
2
. Then by (op+) e
1
+ e
2
, s) n, s)
where n = n
1
+ n
2
, so we are done.
Case (op ). Similar to (op + ).
Case (if). Recall the rule
(if)
e
1
:bool
e
2
:T
e
3
:T
if e
1
then e
2
else e
3
:T
Suppose (, e
1
, bool) (*1), (, e
2
, T) (*2), (, e
3
, T) (*3), e
1
:bool (*4),
e
2
:T (*5) and e
3
:T (*6).
Consider an arbitrary s. Assume dom() dom(s). Write e for if e
1
then e
2
else e
3
.
This e is not a value, so we have to show e, s) has a transition.
case e
1
, s
.e
1
, s) e
1
, s
).
Then by (if3) e, s) if e
1
then e
2
else e
3
, s), so we are done.
case e
1
is a value.
(Now want to use (if1) or (if2), but need to know that e
1
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 By rule induction details omitted.
Using the Lemma and (*4) we have b true, false.e
1
= b.
case b = true. Use (if1).
case b = false. Use (if2).
Case (deref). Recall the rule
(deref)
() = intref
!:int
(This is a leaf it has no e:T premises - so no s to assume).
Consider an arbitrary s with dom() dom(s).
By the condition () = intref we have dom(), so dom(s), so there is
some n with s() = n, so there is an instance of (deref) !, s) n, s).
Cases (assign), (skip), (seq), (while). Left as an exercise.
Slide 87
Lemma: Values of integer type
Lemma 13 for all , e, T, if e:T, e is a value and T = int then for
some n Z we have e = n.
43
Theorem 3 (Type Preservation) If e:T and dom() dom(s) and e, s) e
, s
)
then e
).
Proof First show the second part, using the following lemma.
Lemma 14 If e, s) e
, s
) then dom(s
) = dom(s).
Proof Rule induction on derivations of e, s) e
, s
). Take (e, s, e
, s
) =
(dom(s) = dom(s
)).
All rules are immediate uses of the induction hypothesis except (assign1),
for which we note that if dom(s) then dom(s + ( n)) = dom(s).
Now prove the rst part, ie If e:T and dom() dom(s) and e, s) e
, s
)
then e
:T.
Prove by rule induction on derivations of e, s) e
, s
).
Take (e, s, e
, s
:T.
Case (op+). Recall
(op +) n
1
+ n
2
, s) n, s) if n = n
1
+ n
2
Take arbitrary , T. Suppose n
1
+n
2
:T (*) and dom() dom(s). The last
rule in the derivation of (*) must have been (op+ ), so must have T = int. Then
can use (int) to derive n:T.
Case (op ). Similar.
Case (op1). Recall
(op1)
e
1
, s) e
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
Suppose (e
1
, s, e
1
, s
) (*) and e
1
, s) e
1
, s
). Have to show (e
1
op e
2
, s, e
1
op e
2
, s
).
Take arbitrary , T. Suppose e
1
op e
2
:T and dom() dom(s) (**).
case op = +. The last rule in the derivation of e
1
+ e
2
:T must have been
(op+), so must have T = int, e
1
:int (***) and e
2
:int (****). By the
induction hypothesis (*), (**), and (***) we have e
1
:int. By the (op+)
rule e
1
+ e
2
:T.
case op =. Similar.
Case s (op2) (deref), (assign1), (assign2), (seq1), (seq2), (if1), (if2), (if3), (while).
Left as exercises.
, s
) then either e
, s
such that e
, s
) e
, s
).
Proof Hint: induction along
then T = T
. The proof
is left as Exercise 17.
Theorem 5 (Decidability of typeability) Given , e, one can decide T. e:T.
Theorem 6 (Decidability of type checking) Given , e, T, one can decide e:T.
Proof The implementation gives a type inference algorithm, which, if correct, and to-
gether with Uniqueness, implies both of these results.
44
Slide 88
Summarising Proof Techniques
Determinacy structural induction for e
Progress rule induction for e:T
Type Preservation rule induction for e, s) e
, s
)
Safety mathematical induction on
k
Uniqueness of typing ...
Decidability of typability exhibiting an algorithm
Decidability of checking corollary of other results
3.4 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 The-
orem 1 (Determinacy) for e
1
op e
2
, e
1
; e
2
, while e
1
do e
2
, and if e
1
then e
2
else e
3
.
Exercise 13 Try proving Determinacy for the language with nondeterministic order of
evaluation for e
1
op e
2
(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 induc-
tion over type derivations.
Exercise 17 Prove Theorem 7 (Uniqueness of Typing).
45
4 Functions
Slide 89
Functions L2
Slide 90
Functions, Methods, Procedures...
fun addone x = x+1
public int addone(int x) {
x+1
}
<script type="text/vbscript">
function addone(x)
addone = x+1
end function
</script>
Slide 91
C
7
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.
Slide 93
Functions Syntax
First, extend the L1 syntax:
Variables x X for a set X = x, y, z, ...
Expressions
e ::= ... [ fn x:T e [ e
1
e
2
[ x
Types
T ::= int [ bool [ unit [ T
1
T
2
T
loc
::= intref
Concrete syntax. By convention, application associates to the left, so e
1
e
2
e
3
de-
notes (e
1
e
2
) e
3
, and type arrows associate to the right, so T
1
T
2
T
3
denotes
T
1
(T
2
T
3
). 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 rst-order functions, youd say
A ::= int [ bool [ unit
T ::= A [ A T
T
loc
::= intref
Note that rst-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 (rst-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 Abstract syntax up to alpha conversion, and substitution
In order to express the semantics for functions, we need some auxiliary denitions.
Slide 94
Variable shadowing
(fn x:int (fn x:int x + 1))
class F {
void m() {
int y;
{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 denitions, where you may wish to write a local function
parameter without being aware of what is in the surrounding namespace.
Slide 95
Alpha conversion
In expressions fn x:T e the x is a binder.
inside e, any xs (that arent themselves binders and are not inside
another fn x:T
1
0
x + x
2
dx =
1
0
y + y
2
dy
Slide 96
Alpha conversion free and bound occurrences
In a bit more detail (but still informally):
Say an occurrence of x in an expression e is free if it is not inside any
(fn x:T ...). For example:
17
x + y
fn x:int x + 2
fn x:int x + z
if y then 2 + x else ((fn x:int x + 2)z)
All the other occurrences of x are bound by the closest enclosing
fn 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
Sometimes it is handy to draw in the binding:
Slide 97
Alpha conversion Binding examples
fn x:int x
+ 2
fn x:int x
+ z
fn y:int y
+ z
fn z:int z
/
+z
+ 2)
Slide 98
Alpha Conversion The Convention
Convention: we will allow ourselves to any time at all, in any expression
...(fn x:T e)..., replace the binding x and all occurrences of x that
are bound by that binder, by any other variable so long as that doesnt
change the binding graph.
For example:
fn x:int x
+ z = fn y:int y
+ z ,= fn z:int z
/
+z
fn y:int
+
y
fn z:int
+
z
add pointers (from each x node to the closest enclosing fn x:T node);
remove names of binders and the occurrences they bind
fn :int
+
fn :int
+
fn :int
+
49
Slide 100
fn x:int (fn x:int x + 2)
= fn y:int (fn z:int z + 2) ,= fn z:int (fn y:int z + 2)
fn :int
fn :int
+
fn :int
fn :int
+
Slide 101
(fn x:int x) 7 fn z:int int int (fn y:int z y y)
@
fn :int
_
_
_
_
_
_
_
_
_
_
_
_
_
Slide 102
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 :T nodes you have to count out to to get to its binder.
fn :int (fn :int v
0
+ 2) ,= fn :int (fn :int v
1
+ 2)
fn :int
fn :int
+
fn :int
fn :int
+
Slide 103
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) = x
fv(e
1
op e
2
) = fv(e
1
) fv(e
2
)
fv(fn x:T e) = fv(e) x
Say e is closed if fv(e) = .
If E is a set of expressions, write fv(E) for
e E
fv(e).
(note this denition is alpha-invariant - all our denitions should be)
50
For example
fv(x + y) = x, y
fv(fn x:int x + y) = y
fv(x + (fn x:int x + y)7) = x, y
Full denition of fv(e) is by recursion on the structure of e:
fv(x) = x
fv(fn x:T e) = fv(e) x
fv(e
1
e
2
) = fv(e
1
) fv(e
2
)
fv(n) =
fv(e
1
op e
2
) = fv(e
1
) fv(e
2
)
fv(if e
1
then e
2
else e
3
) = fv(e
1
) fv(e
2
) fv(e
3
)
fv(b) =
fv(skip) =
fv( := e) = fv(e)
fv(!) =
fv(e
1
; e
2
) = fv(e
1
) fv(e
2
)
fv(while e
1
do e
2
) = fv(e
1
) fv(e
2
)
The semantics for functions will involve substituting actual parameters for formal parame-
ters.
Slide 104
Substitution Examples
The semantics for functions will involve substituting actual parameters for
formal parameters.
Write e/xe
. For example
3/x(x x) = (3 3)
3/x((fn x:int x + y)x) = (fn x:int x + y)3
y + 2/x(fn y:int x + y) = fn z:int (y + 2) + z
Note that substitution is a meta-operation its not part of the L2 expression grammar.
The notation used for substitution varies people write 3/xe, or [3/x]e, or e[3/x], or
x 3e, or...
Slide 105
Substitution Denition
Dening that:
e/z x = e if x = z
= x otherwise
e/z (fn x:T e
1
) = fn x:T (e/z e
1
) if x ,= z (*)
and x / fv(e) (*)
e/z (e
1
e
2
) = (e/z e
1
)(e/z e
2
)
...
if (*) is not true, we rst have to pick an alpha-variant of fn x:T e
1
to
make it so (always can)
51
Slide 106
Substitution Example Again
y + 2/x(fn y:int x + y)
= y + 2/x(fn y
:int x + y
) renaming
= fn y
:int y + 2/x(x + y
) as y
,= x and y
/ fv(y + 2)
= fn y
= fn y
:int (y + 2) + y
, except y or x)
Slide 107
Simultaneous substitution
A substitution is a nite partial function from variables to expressions.
Notation: write a as e
1
/x
1
, .., e
k
/x
k
instead of
x
1
e
1
, ..., x
k
e
k
(for the function mapping x
1
to e
1
etc.)
A denition of e is given in the notes.
Write dom() for the set of variables in the domain of ; ran() for the set of expressions
in the range of , ie
dom(e
1
/x
1
, .., e
k
/x
k
) = x
1
, .., x
k
ran(e
1
/x
1
, .., e
k
/x
k
) = e
1
, .., e
k
skip, l ???)
52
Slide 109
Function Behaviour. Choice 1: Call-by-value
Informally: reduce left-hand-side of application to a fn-term; reduce
argument to a value; then replace all occurrences of the formal parameter
in the fn-term by that value.
e = (fn x:unit (l := 1); x)(l := 2)
e, l = 0) (fn x:unit (l := 1); x)skip, l = 2)
(l := 1); skip , l = 2)
skip; skip , l = 1)
skip , l = 1)
This is a common design choice ML, Java. It is a strict semantics fully evaluating the
argument to function before doing the application.
Slide 110
L2 Call-by-value
Values v ::=b [ n [ skip [ fn x:T e
(app1)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(app2)
e
2
, s) e
2
, s
)
v e
2
, s) v e
2
, s
)
(fn) (fn x:T e) v, s) v/xe, s)
Slide 111
L2 Call-by-value reduction examples
(fn x:int fn y:int x + y) (3 + 4) 5 , s)
=
5 , s)
5 , s)
7/x(fn y:int x + y)
5 , s)
=
(fn y:int 7 + y)
5 , s)
7 + 5 , s)
12 , s)
(fn f:int int f 3) (fn x:int (1 + 2) + x)
The rules for these constructs dont touch the store. In a pure functional language,
congurations 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 ecient;
substitutions are simpler to write down so better for implementation and semantics
respectively.
53
Slide 112
Function Behaviour. Choice 2: Call-by-name
Informally: reduce left-hand-side of application to a fn-term; then replace
all occurrences of the formal parameter in the fn-term by the argument.
e = (fn x:unit (l := 1); x) (l := 2)
e, l 0) (l := 1); l := 2, l 0)
skip ; l := 2, l 1)
l := 2 , l 1)
skip , l 2)
This is the foundation of lazy functional languages e.g. Haskell
Slide 113
L2 Call-by-name
(same typing rules as before)
(CBN-app)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(CBN-fn) (fn x:T e)e
2
, s) e
2
/xe, s)
Here, dont evaluate the argument at all if it isnt used
(fn x:unit skip)(l := 2), l 0)
l := 2/xskip , l 0)
= skip , l 0)
but if it is, end up evaluating it repeatedly.
Slide 114
Function Behaviour. Choice 3: Full beta
Allow both left and right-hand sides of application to reduce. At any point
where the left-hand-side has reduced to a fn-term, replace all occurrences
of the formal parameter in the fn-term by the argument. Allow reduction
inside lambdas.
(fn x:int 2 + 2) (fn x:int 4)
Slide 115
L2 Beta
(beta-app1)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(beta-app2)
e
2
, s) e
2
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(beta-fn1) (fn x:T e)e
2
, s) e
2
/xe, s)
(beta-fn2)
e, s) e
, s
)
fn x:T e, s) fn x:T e
, s
)
This reduction relation includes the CBV and CBN relations, and also reduction inside
lambdas.
54
Slide 116
L2 Beta: Example
(fn x:int x + x) (2 + 2)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _
(fn x:int x + x) 4
(2 + 2) + (2 + 2)
..
.
.
.
.
.
.
4 + (2 + 2)
(2 + 2) + 4
.......................
4 + 4
8
Slide 117
Function Behaviour. Choice 4: Normal-order reduction
Leftmost, outermost variant of full beta.
What will (fn x:unit skip) (while true do skip) do in the dierent semantics?
What about (fn x:unit skip) ( := ! + 1)?
Slide 118
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 eects (our
combination of store and CBN is pretty odd). Haskell encourages pure programming, without
eects (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
ecient. The rst time the argument evaluated we overwrite all other copies by that value.
Slide 119
Call-By-Need Example (Haskell)
let notdivby x y = y mod x /= 0
enumFrom n = n : (enumFrom (n+1))
sieve (x:xs) =
x : sieve (filter (notdivby x) xs)
in
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 120
Back to CBV (from now on).
55
4.3 Function Typing
Slide 121
Typing functions (1)
Before, gave the types of store locations; it ranged over TypeEnv
which was the set of all nite partial functions from locations L to T
loc
.
Now, it must also give assumptions on the types of variables: e.g.
l
1
:intref, x:int, y:bool int.
Type environments, TypeEnv2, are nite partial functions from
L X to T
loc
T such that
dom().() T
loc
x dom().(x) T
Notation: if x / dom(), write , x:T for the partial function which
maps x to T but otherwise is like .
Slide 122
Typing functions (2)
(var) x:T if (x) = T
(fn)
, x:T e:T
fn x:T e : T T
(app)
e
1
:T T
e
2
:T
e
1
e
2
:T
Slide 123
Typing functions Example
x:int x:int
(var)
x:int 2:int
(int)
x:int x + 2:int
(op+)
(fn x:int x + 2):int int
(fn)
2:int
(int)
(fn x:int x + 2) 2:int
(app)
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 dierent 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:
l :intref, x:unit 1:int
(int)
l :intref, x:unit (l := 1):unit
(asn)
l :intref, x:unit x:unit
(var)
l :intref, x:unit (l := 1); x:unit
(seq)
l :intref (fn x:unit (l := 1); x):unit unit
(fn)
l :intref 2:int
(int)
l :intref (l := 2):unit
(asn)
l :intref (fn x:unit (l := 1); x) (l := 2):unit
(app)
56
Slide 124
Properties of Typing
We only consider executions of closed programs, with no free variables.
Theorem 15 (Progress) If e closed and e:T and
dom() dom(s) then either e is a value or there exist e
, s
such that
e, s) e
, s
).
Note there are now more stuck congurations, e.g.((3) (4))
Theorem 16 (Type Preservation) If e closed and e:T and
dom() dom(s) and e, s) e
, s
) then e
:T and e
).
Slide 125
Proving Type Preservation
Theorem 16 (Type Preservation) If e closed and e:T and
dom() dom(s) and e, s) e
, s
) then e
:T and e
).
Taking
(e, s, e
, s
) =
, T.
e:T closed(e) dom() dom(s)
:T closed(e
) dom() dom(s
)
we show e, s, e
, s
.e, s) e
, s
) (e, s, e
, s
) by rule
induction.
Slide 126
To prove this one uses:
Lemma 17 (Substitution) If e:T and , x:T e
:T
with
x / dom() then e/xe
:T
.
Determinacy and type inference properties also hold.
Slide 127
Normalization
Theorem 18 (Normalization) In the sublanguage without while loops or
store operations, if e:T and e closed then there does not exist an
innite reduction sequence e, ) e
1
, ) e
2
, ) ...
Proof ? cant do a simple induction, as reduction can make terms grow.
See Pierce Ch.12 (the details are not in the scope of this course).
57
4.4 Local Denitions and Recursive Functions
Slide 128
Local denitions
For readability, want to be able to name denitions, and to restrict their
scope, so add:
e ::= ... [ let val x:T = e
1
in e
2
end
this x is a binder, binding any free occurrences of x in e
2
.
Can regard just as syntactic sugar :
let val x:T = e
1
in e
2
end (fn x:T e
2
)e
1
Slide 129
Local denitions derived typing and reduction rules (CBV)
let val x:T = e
1
in e
2
end (fn x:T e
2
)e
1
(let)
e
1
:T , x:T e
2
:T
(let1)
e
1
, s) e
1
, s
)
let val x:T = e
1
in e
2
end, s) let val x:T = e
1
in e
2
end, s
)
(let2)
let val x:T = v in e
2
end, s) v/xe
2
, s)
Our alpha convention means this really is a local denition there is no way to refer to the
locally-dened variable outside the let val .
x + let val x:int = x in (x + 2) end = x + let val y:int = x in (y + 2) end
Slide 130
Recursive denitions rst attempt
How about
x = (fn y:int if y 1 then y + (x (y +1)) else 0)
where we use x within the denition of x? Think about evaluating x 3.
Could add something like this:
e ::= ... [ let val rec x:T = e in e
end
(here the x binds in both e and e
) then say
let val rec x:int int =
(fn y:int if y 1 then y + (x(y +1)) else 0)
in x 3 end
58
Slide 131
But...
What about
let val rec x = (x, x) in x end ?
Have some rather weird things, eg
let val rec x:int list = 3 :: x in x end
does that terminate? if so, is it equal to
let val rec x:int list = 3 :: 3 :: x in x end ? does
let val rec x:int list = 3 :: (x + 1) in x end terminate?
In a CBN language, it is reasonable to allow this kind of thing, as will only
compute as much as needed. In a CBV language, would usually disallow,
allowing recursive denitions only of functions...
Slide 132
Recursive Functions
So, specialize the previous let val rec construct to
T = T
1
T
2
recursion only at function types
e = fn y:T
1
e
1
and only of function values
e ::= ... [ let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end
(here the y binds in e
1
; the x binds in (fn y:T e
1
) and in e
2
)
(let rec fn)
, x:T
1
T
2
, y:T
1
e
1
:T
2
, x:T
1
T
2
e
2
:T
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end:T
Concrete syntax: In ML can write let fun f (x:T
1
):T
2
= e
1
in e
2
end,
or even let fun f (x) = e
1
in e
2
end, for
let val rec f :T
1
T
2
= fn x:T
1
e
1
in e
2
end.
Slide 133
Recursive Functions Semantics
(letrecfn) let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end
(fn y:T
1
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
1
end)/xe
2
59
For example:
let val rec x:int int =
(fn y:int if y 1 then y + (x(y +1)) else 0)
in
x 3
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
...
Slide 134
Recursive Functions Minimization Example
Below, in the context of the let val rec , x f n nds the smallest n
n
for which f n
evaluates to some m
0.
let val rec x:(int int) int int
= fn f:int int fn z:int if (f z) 1 then x f (z + 1) else z
in
let val f:int int
= (fn z:int if z 3 then (if 3 z then 0 else 1) else 1)
in
x f 0
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
Slide 135
More Syntactic Sugar
Do we need e
1
; e
2
?
No: Could encode by e
1
; e
2
(fn y:unit e
2
)e
1
Do we need while e
1
do e
2
?
No: could encode by while e
1
do e
2
let val rec w:unit unit =
fn y:unit if e
1
then (e
2
; (w skip)) else skip
in
w skip
end
for fresh w and y not in fv(e
1
) fv(e
2
).
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
Slide 136
Implementation
There is an implementation of L2 on the course web page.
See especially Syntax.sml and Semantics.sml. It uses a front
end written with mosmllex and mosmlyac.
The implementation lets you type in L2 expressions and initial stores and watch them
resolve, type-check, and reduce.
Slide 137
Implementation Scope Resolution
datatype expr raw = ...
| Var raw of string
| Fn raw of string
*
type expr
*
expr raw
| App raw of expr raw
*
expr raw
| ...
datatype expr = ...
| Var of int
| Fn of type expr
*
expr
| App of expr
*
expr
resolve scopes : expr raw -> expr
(it raises an exception if the expression has any free variables)
61
Slide 138
Implementation Substitution
subst : expr -> int -> expr -> expr
subst e 0 e substitutes e for the outermost var in e.
(the denition is only sensible if e is closed, but thats ok we only
evaluate whole programs. For a general denition, see [Pierce, Ch. 6])
fun subst e n (Var n1) = if n=n1 then e else Var n1
| subst e n (Fn(t,e1)) = Fn(t,subst e (n+1) e1)
| subst e n (App(e1,e2)) = App(subst e n e1,subst e n e2)
| subst e n (Let(t,e1,e2))
= Let (t,subst e n e1,subst e (n+1) e2)
| subst e n (Letrecfn (tx,ty,e1,e2))
= Letrecfn (tx,ty,subst e (n+2) e1,subst e (n+1) e2)
| ...
If e represents a closed term fn x:T e
1
then e = Fn(t,e1) for t and e1 representing
T and e
1
. If also e represents a closed term e then subst e 0 e1 represents e/xe
1
.
Slide 139
Implementation CBV reduction
reduce (App (e1,e2),s) = (case e1 of
Fn (t,e) =>
(if (is value e2) then
SOME (subst e2 0 e,s)
else
(case reduce (e2,s) of
SOME(e2,s) => SOME(App (e1,e2),s)
| NONE => NONE))
| => (case reduce (e1,s) of
SOME (e1,s)=>SOME(App(e1,e2),s)
| NONE => NONE ))
Slide 140
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))
= (case inftype (#1 gamma, t::(#2 gamma)) e of
SOME t => SOME (func(t,t) )
| NONE => NONE )
inftype gamma (App (e1,e2))
= (case (inftype gamma e1, inftype gamma e2) of
(SOME (func(t1,t1)), SOME t2) =>
if t1=t2 then SOME t1 else NONE
| => NONE )
62
Slide 141
Implementation Closures
Naively implementing substitution is expensive. An efcient
implementation would use closures instead cf. Compiler Construction.
We could give a more concrete semantics, closer to implementation, in
terms of closures, and then prove it corresponds to the original
semantics...
(if you get that wrong, you end up with dynamic scoping, as in original
LISP)
Slide 142
Aside: Small-step vs Big-step Semantics
Throughout this course we use small-step semantics, e, s) e
, s
).
There is an alternative style, of big-step semantics e, s) v, s
), for
example
n, s) n, s)
e
1
, s) n
1
, s
) e
2
, s
) n
2
, s
)
e
1
+ e
2
, s) n, s
) n = n
1
+ n
2
(see the notes from earlier courses by Andy Pitts).
For sequential languages, it doesnt make a major difference. When we
come to add concurrency, small-step is more convenient.
63
4.6 L2: Collected Denition
Syntax
Booleans b B = true, false
Integers n Z = ..., 1, 0, 1, ...
Locations L = l , l
0
, l
1
, l
2
, ...
Variables x X for a set X = x, y, z, ...
Operations op ::=+ [
Types
T ::= int [ bool [ unit [ T
1
T
2
T
loc
::= intref
Expressions
e ::= n [ b [ e
1
op e
2
[ if e
1
then e
2
else e
3
[
:= e [ ! [
skip [ e
1
; e
2
[
while e
1
do e
2
[
fn x:T e [ e
1
e
2
[ x[
let val x:T = e
1
in e
2
end[
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end
In expressions fn x:T e the x is a binder. In expressions let val x:T = e
1
in e
2
end
the x is a binder. In expressions let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end the
y binds in e
1
; the x binds in (fn y:T e
1
) and in e
2
.
Operational Semantics
Say stores s are nite partial functions from L to Z. Values v ::=b [ n [ skip [ fn x:T e
(op +) n
1
+ n
2
, s) n, s) if n = n
1
+ n
2
(op ) n
1
n
2
, s) b, s) if b = (n
1
n
2
)
(op1)
e
1
, s) e
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op2)
e
2
, s) e
2
, s
)
v op e
2
, s) v op e
2
, s
)
(deref) !, s) n, s) if dom(s) and s() = n
(assign1) := n, s) skip, s + n) if dom(s)
(assign2)
e, s) e
, s
)
:= e, s) := e
, s
)
(seq1) skip; e
2
, s) e
2
, s)
(seq2)
e
1
, s) e
1
, s
)
e
1
; e
2
, s) e
1
; e
2
, s
)
64
(if1) if true then e
2
else e
3
, s) e
2
, s)
(if2) if false then e
2
else e
3
, s) e
3
, s)
(if3)
e
1
, s) e
1
, s
)
if e
1
then e
2
else e
3
, s) if e
1
then e
2
else e
3
, s
)
(while)
while e
1
do e
2
, s) if e
1
then (e
2
; while e
1
do e
2
) else skip, s)
(app1)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(app2)
e
2
, s) e
2
, s
)
v e
2
, s) v e
2
, s
)
(fn) (fn x:T e) v, s) v/xe, s)
(let1)
e
1
, s) e
1
, s
)
let val x:T = e
1
in e
2
end, s) let val x:T = e
1
in e
2
end, s
)
(let2)
let val x:T = v in e
2
end, s) v/xe
2
, s)
(letrecfn) let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end
(fn y:T
1
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
1
end)/xe
2
Typing
Type environments, TypeEnv2, are nite partial functions from L X to T
loc
T
such that
dom().() T
loc
x dom().(x) T
(int) n:int for n Z
(bool) b:bool for b true, false
(op +)
e
1
:int
e
2
:int
e
1
+ e
2
:int
(op )
e
1
:int
e
2
:int
e
1
e
2
:bool
(if)
e
1
:bool e
2
:T e
3
:T
if e
1
then e
2
else e
3
:T
(assign)
() = intref e:int
:= e:unit
(deref)
() = intref
!:int
65
(skip) skip:unit
(seq)
e
1
:unit e
2
:T
e
1
; e
2
:T
(while)
e
1
:bool e
2
:unit
while e
1
do e
2
:unit
(var) x:T if (x) = T
(fn)
, x:T e:T
fn x:T e : T T
(app)
e
1
:T T
e
2
:T
e
1
e
2
:T
(let)
e
1
:T , x:T e
2
:T
(couldnt typecheck!).
68
Slide 145
Products typing
(pair)
e
1
:T
1
e
2
:T
2
(e
1
, e
2
):T
1
T
2
(proj1)
e:T
1
T
2
#1 e:T
1
(proj2)
e:T
1
T
2
#2 e:T
2
Slide 146
Products reduction
v ::= ... [ (v
1
, v
2
)
(pair1)
e1, s e
1
, s
(e1, e2), s (e
1
, e2), s
(pair2)
e2, s e
2
, s
2
), s
, s
#1 e, s #1 e
, s
(proj4)
e, s e
, s
#2 e, s #2 e
, s
, s
)
inl e:T, s) inl e
:T, s
)
(case1)
e, s) e
, s
)
case e of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
case e
of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s
)
(case2) case inl v:T of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
v/xe
1
, s)
(inr) and (case3) like (inl) and (case2)
(inr)
e, s) e
, s
)
inr e:T, s) inr e
:T, s
)
(case3) case inr v:T of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
v/ye
2
, s)
Slide 151
Constructors and Destructors
type constructors destructors
T T fn x:T e
T T ( , ) #1 #2
T + T inl ( ) inr ( ) case
bool true false if
70
Slide 152
Proofs as programs: The Curry-Howard correspondence
(var) , x:T x:T
(fn)
, x:T e:T
fn x:T e : T T
(app)
e
1
:T T
e
2
:T
e
1
e
2
:T
, P P
, P P
P P
P P
P
P
Slide 153
Proofs as programs: The Curry-Howard correspondence
(var) , x:T x:T
(fn)
, x:T e:T
fn x:T e : T T
(app)
e1:T T
e2:T
e1 e2:T
(pair)
e1:T1 e2:T2
(e1, e2):T1 T2
(proj1)
e:T1 T2
#1 e:T1
(proj2)
e:T1 T2
#2 e:T2
(inl)
e:T1
inl e:T1 + T2:T1 + T2
(inr), (case), (unit), (zero), etc.. but not (letrec)
, P P
, P P
P P
P P
P
P
P1 P2
P1 P2
P1 P2
P1
P1 P2
P2
P1
P1 P2
The typing rules for a pure language correspond to the rules for a sequent calculus.
5.2 Datatypes and Records
Slide 154
ML Datatypes
Datatypes in ML generalize both sums and products, in a sense
datatype IntList = Null of unit
| Cons of Int
*
IntList
is (roughly!) like saying
IntList = unit + (Int
*
IntList)
In L3 you cannot dene 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
Slide 155
Records
A generalization of products.
Take eld labels
Labels lab LAB for a set LAB = p, q, ...
T ::= ... [ lab
1
:T
1
, .., lab
k
:T
k
(recordproj)
e:lab
1
:T
1
, .., lab
k
:T
k
#lab
i
e:T
i
Here the eld 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.
Slide 157
Records reduction
v ::= ... [ lab
1
= v
1
, .., lab
k
= v
k
(record1)
e
i
, s) e
i
, s
)
lab
1
= v
1
, .., lab
i
= e
i
, .., lab
k
= e
k
, s)
lab
1
= v
1
, .., lab
i
= e
i
, .., lab
k
= e
k
, s
)
(record2) #lab
i
lab
1
= v
1
, .., lab
k
= v
k
, s) v
i
, s)
(record3)
e, s) e
, s
)
#lab
i
e, s) #lab
i
e
, s
)
72
5.3 Mutable Store
Slide 158
Mutable Store
Most languages have some kind of mutable store. Two main choices:
1 What weve got in L1 and L2:
e ::= ... [ := e [ ! [ x
locations store mutable values
variables refer to a previously-calculated value, immutably
explicit dereferencing and assignment operators for locations
fn x:int l := (!l ) + x
Slide 159
2 In C and Java,
variables let you refer to a previously calculated value and let you
overwrite that value with another.
implicit dereferencing,
void foo(x:int) {
l = l + x
...}
have some limited type machinery to limit mutability.
pros and cons: ....
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
Slide 160
References
T ::= ... [ T ref
T
loc
::= intref T ref
e ::= ... [ := e [ !
[ e
1
:= e
2
[ !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
Slide 161
References Typing
(ref)
e:T
ref e : T ref
(assign)
e
1
:T ref e
2
:T
e
1
:= e
2
:unit
(deref)
e:T ref
!e:T
(loc)
() = T ref
:T ref
Slide 162
References Reduction
A location is a value:
v ::= ... [
Stores s were nite partial maps from L to Z. From now on, take them to
be nite partial maps from L to the set of all values.
(ref1) ref v, s) , s + v) / dom(s)
(ref2)
e, s) e
, s
)
ref e, s) ref e
, s
)
Slide 163
(deref1) !, s) v, s) if dom(s) and s() = v
(deref2)
e, s) e
, s
)
!e, s) !e
, s
)
(assign1) := v, s) skip, s + v) if dom(s)
(assign2)
e, s) e
, s
)
:= e, s) := e
, s
)
(assign3)
e, s) e
, s
)
e := e
2
, s) e
:= e
2
, s
)
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.
Slide 164
Type-checking the store
For L1, our type properties used dom() dom(s) to express the
condition all locations mentioned in exist in the store s.
Now need more: for each dom(s) need that s() is typable.
Moreover, s() might contain some other locations...
Slide 165
Type-checking the store Example
Consider
e = let val x:(int int) ref = ref(fn z:int z) in
(x := (fn z:int if z 1 then z + ((!x) (z +1)) else 0);
(!x) 3) end
which has reductions
e, )
e
1
, l
1
(fn z:int z))
e
2
, l
1
(fn z:int if z 1 then z + ((!l
1
) (z +1)) else 0))
6, ...)
For reference, e
1
and e
2
are
e
1
= l
1
:= (fn z:int if z 1 then z + ((!l
1
) (z +1)) else 0);
((!l
1
) 3)
e
2
= skip; ((!l
1
) 3)
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.
Slide 166
Denition 19 (Well-typed store) Let s if dom() = dom(s) and if
for all dom(s), if () = T ref then s():T.
Theorem 20 (Progress) If e closed and e:T and s then either
e is a value or there exist e
, s
such that e, s) e
, s
).
Theorem 21 (Type Preservation) If e closed and e:T and s
and e, s) e
, s
) then e
with disjoint
domain to we have ,
:T and ,
.
Theorem 22 (Type Safety) If e closed and e:T and s and
e, s)
, s
) then either e
, s
such
that e
, s
) e
, s
).
Slide 167
Implementation
The collected denition so far is in the notes, called L3.
It is again a Moscow ML fragment (modulo the syntax for T + 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 dierent style for dening operational semantics,
collecting together many of the context rules into a single (eval) rule that uses a denition
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.
Slide 168
Evaluation Contexts
Dene evaluation contexts
E ::= op e [ v op [ if then e else e [
; e [
e [ v [
let val x:T = in e
2
end [
( , e) [ (v, ) [ #1 [ #2 [
inl :T [ inr :T [
case of inl (x:T) e [ inr (x:T) e [
lab
1
= v
1
, .., lab
i
= , .., lab
k
= e
k
[ #lab [
:= e [ v := [ ! [ ref
Slide 169
and have the single context rule
(eval)
e, s) e
, s
)
E[e], s) E[e
], s
)
replacing the rules (all those with 1 premise) (op1), (op2), (seq2), (if3),
(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 denitions of dene the same relation.
Slide 170
A Little History
Formal logic 1880
Untyped lambda calculus 1930s
Simply-typed lambda calculus 1940s
Fortran 1950s
Curry-Howard, Algol 60, Algol 68, SECD machine (64) 1960s
Pascal, Polymorphism, ML, PLC 1970s
Structured Operational Semantics 1981
Standard ML denition 1985
Haskell 1987
Subtyping 1980s
Module systems 1980
Object calculus 1990
Typed assembly and intermediate languages 1990
And now? module systems, distribution, mobility, reasoning about objects, security, typed compilation,.......
76
5.5 L3: Collected denition
L3 syntax
Booleans b B = true, false
Integers n Z = ..., 1, 0, 1, ...
Locations L = l , l
0
, l
1
, l
2
, ...
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 [ T
1
T
2
[T
1
T
2
[T
1
+ T
2
[lab
1
:T
1
, .., lab
k
:T
k
[T ref
Expressions
e ::= n [ b [ e
1
op e
2
[ if e
1
then e
2
else e
3
[
e
1
:= e
2
[ !e [ ref e [ [
skip [ e
1
; e
2
[
while e
1
do e
2
[
fn x:T e [ e
1
e
2
[ x[
let val x:T = e
1
in e
2
end[
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end[
(e
1
, e
2
) [ #1 e [ #2 e[
inl e:T [ inr e:T [
case e of inl (x
1
:T
1
) e
1
[ inr (x
2
:T
2
) e
2
[
lab
1
= e
1
, .., lab
k
= e
k
[ #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 = e
1
in e
2
end
the x is a binder. In expressions let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end the
y binds in e
1
; the x binds in (fn y:T e
1
) and in e
2
. In case e of inl (x
1
:T
1
) e
1
[
inr (x
2
:T
2
) e
2
the x
1
binds in e
1
and the x
2
binds in e
2
.
L3 semantics
Stores s are nite partial maps from L to the set of all values.
Values v ::=b [ n [ skip [ fn x:T e[(v
1
, v
2
)[inl v:T [ inr v:T[lab
1
= v
1
, .., lab
k
= v
k
[
(op +) n
1
+ n
2
, s) n, s) if n = n
1
+ n
2
(op ) n
1
n
2
, s) b, s) if b = (n
1
n
2
)
(op1)
e
1
, s) e
1
, s
)
e
1
op e
2
, s) e
1
op e
2
, s
)
(op2)
e
2
, s) e
2
, s
)
v op e
2
, s) v op e
2
, s
)
(seq1) skip; e
2
, s) e
2
, s)
(seq2)
e
1
, s) e
1
, s
)
e
1
; e
2
, s) e
1
; e
2
, s
)
77
(if1) if true then e
2
else e
3
, s) e
2
, s)
(if2) if false then e
2
else e
3
, s) e
3
, s)
(if3)
e
1
, s) e
1
, s
)
if e
1
then e
2
else e
3
, s) if e
1
then e
2
else e
3
, s
)
(while)
while e
1
do e
2
, s) if e
1
then (e
2
; while e
1
do e
2
) else skip, s)
(app1)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(app2)
e
2
, s) e
2
, s
)
v e
2
, s) v e
2
, s
)
(fn) (fn x:T e) v, s) v/xe, s)
(let1)
e
1
, s) e
1
, s
)
let val x:T = e
1
in e
2
end, s) let val x:T = e
1
in e
2
end, s
)
(let2)
let val x:T = v in e
2
end, s) v/xe
2
, s)
(letrecfn) let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
2
end
(fn y:T
1
let val rec x:T
1
T
2
= (fn y:T
1
e
1
) in e
1
end)/xe
2
(pair1)
e
1
, s) e
1
, s
)
(e
1
, e
2
), s) (e
1
, e
2
), s
)
(pair2)
e
2
, s) e
2
, s
)
(v
1
, e
2
), s) (v
1
, e
2
), s
)
(proj1) #1(v
1
, v
2
), s) v
1
, s) (proj2) #2(v
1
, v
2
), s) v
2
, s)
(proj3)
e, s) e
, s
)
#1 e, s) #1 e
, s
)
(proj4)
e, s) e
, s
)
#2 e, s) #2 e
, s
)
(inl)
e, s) e
, s
)
inl e:T, s) inl e
:T, s
)
(case1)
e, s) e
, s
)
case e of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
case e
of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s
)
(case2) case inl v:T of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
v/xe
1
, s)
(inr) and (case3) like (inl) and (case2)
78
(inr)
e, s) e
, s
)
inr e:T, s) inr e
:T, s
)
(case3) case inr v:T of inl (x:T
1
) e
1
[ inr (y:T
2
) e
2
, s)
v/ye
2
, s)
(record1)
e
i
, s) e
i
, s
)
lab
1
= v
1
, .., lab
i
= e
i
, .., lab
k
= e
k
, s)
lab
1
= v
1
, .., lab
i
= e
i
, .., lab
k
= e
k
, s
)
(record2) #lab
i
lab
1
= v
1
, .., lab
k
= v
k
, s) v
i
, s)
(record3)
e, s) e
, s
)
#lab
i
e, s) #lab
i
e
, s
)
(ref1) ref v, s) , s + v) / dom(s)
(ref2)
e, s) e
, s
)
ref e, s) ref e
, s
)
(deref1) !, s) v, s) if dom(s) and s() = v
(deref2)
e, s) e
, s
)
!e, s) !e
, s
)
(assign1) := v, s) skip, s + v) if dom(s)
(assign2)
e, s) e
, s
)
:= e, s) := e
, s
)
(assign3)
e, s) e
, s
)
e := e
2
, s) e
:= e
2
, s
)
L3 Typing
Type environments, TypeEnv2, are nite partial functions from L X to T
loc
T
such that
dom().() T
loc
x dom().(x) T
(int) n:int for n Z
(bool) b:bool for b true, false
(op +)
e
1
:int
e
2
:int
e
1
+ e
2
:int
(op )
e
1
:int
e
2
:int
e
1
e
2
:bool
(if)
e
1
:bool e
2
:T e
3
:T
if e
1
then e
2
else e
3
:T
79
(skip) skip:unit
(seq)
e
1
:unit e
2
:T
e
1
; e
2
:T
(while)
e
1
:bool e
2
:unit
while e
1
do e
2
:unit
(var) x:T if (x) = T
(fn)
, x:T e:T
fn x:T e : T T
(app)
e
1
:T T
e
2
:T
e
1
e
2
:T
(let)
e
1
:T , x:T e
2
:T
(recordproj)
e:lab
1
:T
1
, .., lab
k
:T
k
#lab
i
e:T
i
80
(ref)
e:T
ref e : T ref
(assign)
e
1
:T ref e
2
:T
e
1
:= e
2
:unit
(deref)
e:T ref
!e:T
(loc)
() = T ref
:T ref
5.6 Exercises
Exercise 27 Prove Theorem 14: Type Preservation for L3.
Exercise 28 Labelled variant types are a generalization of sum types, just as records
are a generalization of products. Design abstract syntax, type rules and evaluation rules for
labelled variants, analogously to the way in which records generalise products.
Exercise 29 Design type rules and evaluation rules for ML-style exceptions. Start
with exceptions that do not carry any values. Hint 1: take care with nested handlers within
recursive functions. Hint 2: you might want to express your semantics using evaluation
contexts.
Exercise 30 Extend the L2 implementation to cover all of L3.
81
6 Subtyping and Objects
Slide 171
Subtyping and Objects
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 dierent sorting code for,
e.g., int lists and int int lists.
Slide 172
Polymorphism
Ability to use expressions at many different types.
Ad-hoc polymorphism (overloading).
e.g. in Moscow ML the built-in + can be used to add two integers or to
add two reals. (see Haskell type classes)
Parametric Polymorphism as in ML. See the Part II Types course.
can write a function that for any type takes an argument of type
list and computes its length (parametric uniform in whatever is)
Subtype polymorphism as in various OO languages. See here.
Dating back to the 1960s (Simula etc); formalized in 1980,1984,...
Slide 173
Subtyping Motivation
Recall
(app)
e
1
:T T
e
2
:T
e
1
e
2
:T
so cant type
, (fn x:p:int #p x) p = 3, q = 4 : int
even though were giving the function a better argument, with more
structure, than it needs.
82
Slide 174
Subsumption
Better? Any value of type p:int, q:int can be used wherever a value
of type p:int is expected. (*)
Introduce a subtyping relation between types, written T <: T
, read as
T is a subtype of T
).
Will dene it on the next slides, but it will include
p:int, q:int <: p:int <:
Introduce a subsumption rule
(sub)
e:T T <: T
e:T
(s-re)
T <: T
(s-trans)
T <: T
<: T
T <: T
Slide 177
Subtyping Records
Forgetting elds on the right:
lab
1
:T
1
, .., lab
k
:T
k
, lab
k+1
:T
k+1
, .., lab
k+k
:T
k+k
<: (s-record-width)
lab
1
:T
1
, .., lab
k
:T
k
1
.. T
k
<: T
k
lab
1
:T
1
, .., lab
k
:T
k
<: lab
1
:T
1
, .., lab
k
:T
Combining these:
{p:int, q:int} <: {p:int}
(s-record-width)
{r:int} <: {}
(s-record-width)
{x:{p:int, q:int}, y:{r:int}} <: {x:{p:int}, y:{}}
(s-record-depth)
83
Another example:
x:p:int, q:int, y:r:int <: x:p:int, q:int
(s-rec-w)
p:int, q:int <: p:int
(s-rec-w)
x:p:int, q:int <: x:p:int
(s-rec-d)
x:p:int, q:int, y:r:int <: x:p:int
(s-trans)
Slide 178
Allowing reordering of elds:
(s-record-order)
a permutation of 1, .., k
lab
1
:T
1
, .., lab
k
:T
k
<: lab
(1)
:T
(1)
, .., lab
(k)
:T
(k)
1
<: T
1
T
2
<: T
2
T
1
T
2
<: T
1
T
2
contravariant on the left of
covariant on the right of (like (s-record-depth))
Slide 180
If f :T
1
T
2
then we can give f any argument which is a subtype of
T
1
; we can regard the result of f as any supertype of T
2
. e.g., for
f = fn x:p:int p = #p x, q = 28
we have
f :p:int p:int, q:int
f :p:int p:int
f :p:int, q:int p:int, q:int
f :p:int, q:int p:int
as
p:int, q:int <: p:int
Slide 181
On the other hand, for
fn x:p:int, q:int p = (#p x) + (#q x)
we have
f :p:int, q:int p:int
, f :p:int T for any T
, f :T p:int, q:int for any T
84
Slide 182
Subtyping Products
Just like (s-record-depth)
(s-pair)
T
1
<: T
1
T
2
<: T
2
T
1
T
2
<: T
1
T
2
Subtyping Sums
Exercise.
Slide 183
Subtyping References
Are either of these any good?
T <: T
T ref <: T
ref
T
<: T
T ref <: T
ref
No...
Slide 184
Semantics
No change (note that weve not changed the expression grammar).
Properties
Have Type Preservation and Progress.
Implementation
Type inference is more subtle, as the rules are no longer syntax-directed.
Getting a good runtime implementation is also tricky, especially with eld
re-ordering.
Slide 185
Subtyping Down-casts
The subsumption rule (sub) permits up-casting at any point. How about
down-casting? We could add
e ::= ... [ (T)e
with typing rule
e:T
(T)e:T
then you need a dynamic type-check...
This gives exibility, but at the cost of many potential run-time errors.
Many uses might be better handled by Parametric Polymorphism, aka
Generics. (cf. work by Martin Odersky at EPFL, Lausanne, now in Java
1.5)
The following development is taken from [Pierce, Chapter 18], where you can nd more
details (including a treatment of self and a direct semantics for a featherweight fragment
of Java).
85
Slide 186
(Very Simple) Objects
let val c:get:unit int, inc:unit unit =
let val x:int ref = ref 0 in
get = fn y:unit !x,
inc = fn y:unit x := 1 + !x
end
in
(#inc c)(); (#get c)()
end
Counter = get:unit int, inc:unit unit.
Slide 187
Using Subtyping
let val c:get:unit int, inc:unit unit, reset:unit unit =
let val x:int ref = ref 0 in
get = fn y:unit !x,
inc = fn y:unit x := 1 + !x,
reset = fn y:unit x := 0
end
in
(#inc c)(); (#get c)()
end
ResetCounter = {get:unit int, inc:unit unit, reset:unit unit}
<: Counter = {get:unit int, inc:unit unit}.
Slide 188
Object Generators
let val newCounter:unit get:unit int, inc:unit unit =
fn y:unit
let val x:int ref = ref 0 in
get = fn y:unit !x,
inc = fn y:unit x := 1 + !x
end
in
(#inc (newCounter ())) ()
end
and onwards to simple classes...
86
Slide 189
Reusing Method Code (Simple Classes)
Recall Counter = get:unit int, inc:unit unit.
First, make the internal state into a record.
CounterRep = p:int ref.
let val counterClass:CounterRep Counter =
fn x:CounterRep
get = fn y:unit !(#p x),
inc = fn y:unit (#p x) := 1 + !(#p x)
let val newCounter:unit Counter =
fn y:unit
let val x:CounterRep = p = ref 0 in
counterClass x
Slide 190
Reusing Method Code (Simple Classes)
let val resetCounterClass:CounterRep ResetCounter =
fn x:CounterRep
let val super = counterClass x in
get = #get super,
inc = #inc super,
reset = fn y:unit (#p x) := 0
CounterRep = p:int ref.
Counter = get:unit int, inc:unit unit.
ResetCounter = get:unit int, inc:unit unit, reset:unit
unit.
Slide 191
Reusing Method Code (Simple Classes)
class Counter
{ protected int p;
Counter() { this.p=0; }
int get () { return this.p; }
void inc () { this.p++ ; }
};
class ResetCounter
extends Counter
{ void reset () {this.p=0;}
};
87
Slide 192
Subtyping Structural vs Named
A
= with p:int
A
= A
with q:bool
A
= A
with r:int
{}
{p:int}
{p:int, q:bool}
.
.
.
.
.
.
{p:int, r:int}
Object (ish!)
A
6.1 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 183, 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
7 Semantic Equivalence
Slide 193
Semantic Equivalence
Slide 194
2 + 2
?
4
In what sense are these two expressions the same?
They have different abstract syntax trees.
They have different reduction sequences.
But, youd hope that in any program you could replace one by the other
without affecting the result....
2+2
0
e
sin(x)
dx =
4
0
e
sin(x)
dx
Slide 195
How about (l := 0; 4)
?
(l := 1; 3 + !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
C[l := 0; 4] = (l := 0; 4) + !l
,
C[l := 1; 3 + !l ] = (l := 1; 3+!l ) + !l
On the other hand, consider
(l := !l + 1); (l := !l 1)
?
(l := !l )
Slide 196
Those were all particular expressions may want to know that some
general laws are valid for all e
1
, e
2
, .... How about these:
e
1
; (e
2
; e
3
)
?
(e
1
; e
2
); e
3
(if e
1
then e
2
else e
3
); e
?
if e
1
then e
2
; e else e
3
; e
e; (if e
1
then e
2
else e
3
)
?
if e
1
then e; e
2
else e; e
3
e; (if e
1
then e
2
else e
3
)
?
if e; e
1
then e
2
else e
3
Slide 197
let val x = ref 0 in fn y:int (x := !x + y); !x
?
, s) b, s) if b = ( =
)
Slide 199
f = let val x = ref 0 in
let val y = ref 0 in
fn z:int ref if z = x then y else x
end end
g = let val x = ref 0 in
let val y = ref 0 in
fn z:int ref if z = y then y else x
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
Slide 200
With a good notion of semantic equivalence, we might:
1. understand what a program is
2. prove that some particular expression (say an efcient algorithm) is
equivalent to another (say a clear specication)
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
90
Slide 201
What does it mean for to be good?
1. programs that result in observably-different values (in some initial
store) must not be equivalent
( s, s
1
, s
2
, v
1
, v
2
.e
1
, s)
v
1
, s
1
) e
2
, s)
v
2
, s
2
)
v
1
,= v
2
) e
1
, e
2
2. programs that terminate must not be equivalent to programs that dont
3. must be an equivalence relation
e e, e
1
e
2
e
2
e
1
, e
1
e
2
e
3
= e
1
e
3
4. must be a congruence
if e
1
e
2
then for any context C we must have C[e
1
] C[e
2
]
5. should relate as many programs as possible subject to the above.
Slide 202
Semantic Equivalence for L1
Consider Typed L1 again.
Dene e
1
T
e
2
to hold iff forall s such that dom() dom(s), we
have e
1
:T, e
2
:T, and either
(a) e
1
, s)
and e
2
, s)
, or
(b) for some v, s
we have e
1
, s)
v, s
) and
e
2
, s)
v, s
).
In this denition, part (b), we require that e
1
and e
2
result in the same value and moreover
the same store. This is because, if we were to equate two programs e
1
and e
2
that result
in dierent stores say s
1
(l ),= s
2
(l ) then we could distinguish them using the following
contexts, and the semantic equivalence would not be a congruence.
Slide 203
If T = unit then C = ; !l .
If T = bool then C = if then !l else !l .
If T = int then C = l
1
:= ; !l .
Slide 204
Congruence for Typed L1
The L1 contexts are:
C ::= op e
2
[ e
1
op [
if then e
2
else e
3
[ if e
1
then else e
3
[
if e
1
then e
2
else [
:= [
; e
2
[ e
1
; [
while do e
2
[ while e
1
do
Say
T
e
2
we have,
for all C and T
, if C[e
1
]:T
and C[e
2
]:T
then
C[e
1
]
T
C[e
2
].
91
Slide 205
Theorem 24 (Congruence for L1)
T
.
Using the reduction rules construct a sequence of C[e
].
Theorem 24 (Congruence for L1)
T
, := e:T
and := e
:T
. By
examining the typing rules we have T = int and T
= unit.
To show := e
T
:= e
), := e
:T
), and either
1. := e, s)
and := e
, s)
, or
2. for some v, s
we have := e, s)
v, s
) and := e
, s)
v, s
).
Consider the possible reduction sequences of a state := e, s). Recall that (by
examining the reduction rules), if := e, s) e
1
, s
1
) then either that is an
instance of (assign1), with n.e = n dom(s) e
1
= skip s
= s + n,
or it is an instance of (assign2), with e
1
.e, s) e
1
, s
1
) e
1
= ( := e
1
). We
know also that skip, s) does not reduce.
Now (using Determinacy), for any e and s we have either
Case: := e, s)
, i.e.
:= e, s) e
1
, s
1
) e
2
, s
2
) ...
hence all these must be instances of (assign2), with
e, s) e
1
, s
1
) e
2
, s
2
) ...
and e
1
= ( := e
1
), e
2
= ( := e
2
),...
Case: ( := e, s)
), i.e.
:= e, s) e
1
, s
1
) e
2
, s
2
)... e
k
, s
k
) ,
hence all these must be instances of (assign2) except the last, which must be
an instance of (assign1), with
e, s) e
1
, s
1
) e
2
, s
2
) ... e
k1
, s
k1
)
and e
1
= ( := e
1
), e
2
= ( := e
2
),..., e
k1
= ( := e
k1
) and for some n we
have e
k1
= n, e
k
= skip, and s
k
= s
k1
+ 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 := e, s)
, s), so (using
(assign2)) there is an innite reduction sequence of := e
, s).
92
On the other hand, if ( := e, s)
n, s
k1
) and := e, s) skip, s
k1
+ n). By
e
T
we have e
, s)
n, s
k1
). Then using (assign1) := e
, s)
:= n, s
k1
) skip, s
k1
+ n = e
k
, s
k
) as required.
Slide 206
Back to the Examples
We dened e
1
T
e
2
iff for all s such that dom() dom(s), we have
e
1
:T, e
2
:T, and either
1. e
1
, s)
and e
2
, s)
, or
2. for some v, s
we have e
1
, s)
v, s
) and
e
2
, s)
v, s
).
So:
2 + 2
int
4 for any
(l := 0; 4) ,
int
(l := 1; 3 + !l ) for any
(l := !l + 1); (l := !l 1)
unit
(e
1
; e
2
); e
3
for any , T, e
1
, e
2
and e
3
such that e
1
:unit, e
2
:unit, and e
3
:T
Conjecture 26
((if e
1
then e
2
else e
3
); e)
T
(if e
1
then e
2
; e else e
3
; e) for any
, T, e, e
1
, e
2
and e
3
such that e
1
:bool, e
2
:unit, e
3
:unit,
and e:T
Conjecture 27
(e; (if e
1
then e
2
else e
3
))
T
(if e
1
then e; e
2
else e; e
3
) for any
, T, e, e
1
, e
2
and e
3
such that e:unit, e
1
:bool, e
2
:T,
and e
3
:T
Slide 208
Q: Is a typed expression e:T, e.g.
l :intref if !l 0 then skip else (skip; l := 0):unit:
1. a list of tokens [ IF, DEREF, LOC "l", GTEQ, ..];
2. an abstract syntax tree if then else
skip ;
!l
0 skip l :=
0
;
3. the function taking store s to the reduction sequence
e, s) e
1
, s
1
) e
2
, s
2
) ...; or
4. the equivalence class e
[ e
T
, or is
v, s
), if e, s)
v, s
)
(the Determinacy theorem tells us that this is a denition of a function).
Slide 209
Suppose e
1
:unit and e
2
:unit.
When is e
1
; e
2
unit
e
2
; e
1
?
A sucient condition: they dont mention any locations (but not necessary... e.g. if e
1
does
but e
2
doesnt)
93
7.1 Contextual equivalence
The denition of semantic equivalence works ne for L1. However, when we come to L2 and
L3, the simple notion does not give a congruence.
Here is a basic denition of an equivalence for L3.
Slide 210
Contextual equivalence for L3
Denition 28 Consider typed L3 programs, e
1
:T and e
2
:T.
We say that they are contextually equivalent if, for every context C such
that C[e
1
]:unit and C[e
2
]:unit, we have either
(a) C[e
1
], )
and C[e
2
], )
, or
(b) for some s
1
and s
2
we have C[e
1
], )
skip, s
1
) and
C[e
2
], )
skip, s
2
).
Notice that contextual equivalence is a congruence by denition.
Contextual equivalence is undecidable in general. An important research topic is nding
techniques for proving contextual equivalence.
7.2 Exercises
Exercise 36 Prove some of the other cases of the Congruence theorem for semantic
equivalence in L1.
Exercise 37 Prove that if
1
e
1
:unit and
2
e
2
:unit in L1, and
1
is disjoint from
2
, then e
1
; e
2
unit
e
2
; e
1
where =
1
2
Exercise 38 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: nd a context that will
diverge for one of them, but not for the other.
94
8 Concurrency
Slide 211
Concurrency
Slide 212
Our focus so far has been on semantics for sequential computation. But
the world is not sequential...
hardware is intrinsically parallel (ne-grain, across words, to
coarse-grain, e.g. multiple execution units)
multi-processor machines
multi-threading (perhaps on a single processor)
networked machines
Slide 213
Problems
the state-spaces of our systems become large, with the combinatorial
explosion with n threads, each of which can be in 2 states, the
system has 2
n
states.
the state-spaces become complex
computation becomes nondeterministic (unless synchrony is
imposed), as different threads operate at different speeds.
parallel components competing for access to resources may deadlock
or suffer starvation. Need mutual exclusion between components
accessing a resource.
Slide 214
More Problems!
partial failure (of some processes, of some machines in a network, of
some persistent storage devices). Need transactional mechanisms.
communication between different environments (with different local
resources (e.g. different local stores, or libraries, or...)
partial version change
communication between administrative regions with partial trust (or,
indeed, no trust ); protection against mailicious attack.
dealing with contingent complexity (embedded historical accidents;
upwards-compatible deltas)
95
Slide 215
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 ne distinctions. Primarily (1) to boost
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 dene the simplest possible concurrent language, call it L1, and
explore a few issues. Youve seen most of them informally in C&DS.
Slide 216
Booleans b B = true, false
Integers n Z = ..., 1, 0, 1, ...
Locations L = l , l
0
, l
1
, l
2
, ...
Operations op ::=+ [
Expressions
e ::= n [ b [ e
1
op e
2
[ if e
1
then e
2
else e
3
[
:= e [ ! [
skip [ e
1
; e
2
[
while e
1
do e
2
[
e
1
e
2
T ::= int [ bool [ unit [ proc
T
loc
::= intref
Slide 217
Parallel Composition: Typing and Reduction
(thread)
e:unit
e:proc
(parallel)
e
1
:proc e
2
:proc
e
1
e
2
:proc
(parallel1)
e
1
, s) e
1
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
(parallel2)
e
2
, s) e
2
, s
)
e
1
e
2
, s) e
1
e
2
, s
)
Slide 218
Parallel Composition: Design Choices
threads dont return a value
threads dont have an identity
termination of a thread cannot be observed within the language
threads arent partitioned into processes or machines
threads cant be killed externally
96
Slide 219
Threads execute asynchronously the semantics allows any interleaving
of the reductions of the threads.
All threads can read and write the shared memory.
() l := 2, {l 1}
() (), {l 2}
l := 1 l := 2, {l 0}
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
l := 1 (), {l 2}
() (), {l 1}
NB from here on, we are using () instead of skip thats the ML syntax.
Slide 220
But, assignments and dereferencing are atomic. For example,
l := 3498734590879238429384 [ l := 7, l 0)
will reduce to a state with l either 3498734590879238429384 or 7, not
something with the rst word of one and the second word of the other.
Implement?
But but, in (l := e) e
can be
interleaved.
Think of (l := 1+!l ) (l := 7 + !l ) there are races....
97
T
h
e
b
e
h
a
v
i
o
u
r
o
f
(
l
:
=
1
+
!
l
)
(
l
:
=
7
+
!
l
)
f
o
r
t
h
e
i
n
i
t
i
a
l
s
t
o
r
e
:
() (l := 7+!l ), {l 1}
r
() (), {l 8}
(l := 1) (l := 7+!l ), {l 0}
r
.
.
.
.
.
.
.
.
.
.
.
.
.
.
() (l := 7 + 0), {l 1}
+
(l := 1 + 0) (l := 7+!l ), {l 0}
r
(l := 1) (l := 7 + 0), {l 0}
+
.
.
.
.
.
.
.
.
.
.
.
.
.
.
() (l := 7), {l 1}
w
() (), {l 7}
(l := 1+!l ) (l := 7+!l ), {l 0}
r
(l := 1 + 0) (l := 7 + 0), {l 0}
+
.
.
.
.
.
.
.
.
.
.
.
.
.
.
+
(l := 1) (l := 7), {l 0}
w
.
.
.
.
.
.
.
.
.
.
.
.
.
w
(l := 1+!l ) (l := 7 + 0), {l 0}
r
(l := 1 + 0) (l := 7), {l 0}
+
.
.
.
.
.
.
.
.
.
.
.
.
.
.
w
l := 1 (), {l 7}
w
() (), {l 1}
(l := 1+!l ) (l := 7), {l 0}
r
.
.
.
.
.
.
.
.
.
.
.
.
.
.
w
l := 1 + 0 (), {l 7}
+
.
.
.
.
.
.
.
.
.
.
.
.
.
l := 1+!l (), {l 7}
r
() (), {l 8}
9
8
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...
Slide 221
Morals
There is a combinatorial explosion.
Drawing state-space diagrams only works for really tiny examples we
need better techniques for analysis.
Almost certainly you (as the programmer) didnt want all those 3
outcomes to be possible need better idioms or constructs for
programming.
Slide 222
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
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 Specication. In G.
Nelson, editor, System Programming with Modula-3, chapter 5, pages 119-129. Prentice-
Hall, 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).
Slide 223
Adding Primitive Mutexes
Mutex names m M= m, m
1
, ...
Congurations e, s, M) where M:MB is the mutex state
Expressions e ::=... [ lock m [ unlock m
(lock)
lock m:unit
(unlock)
unlock m:unit
(lock) lock m, s, M) (), s, M +m true) if M(m)
(unlock) unlock m, s, M) (), s, M +m false)
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.
99
Slide 224
Need to adapt all the other semantic rules to carry the mutex state M
around. For example, replace
(op2)
e
2
, s) e
2
, s
)
v op e
2
, s) v op e
2
, s
)
by
(op2)
e
2
, s, M) e
2
, s
, M
)
v op e
2
, s, M) v op e
2
, s
, M
)
Slide 225
Using a Mutex
Consider
e = (lock m; l := 1+!l ; unlock m) (lock m; l := 7 + !l ; unlock m)
The behaviour of e, s, M), with the initial store s = l 0 and initial
mutex state M
0
= m M.false, is:
(l := 1+!l ; unlock m) (lock m; l := 7 + !l ; unlock m), s, M
e, s, M0
lock m
lock m
() (), {l 8}, M
(lock m; l := 1+!l ; unlock m) (l := 7 + !l ; unlock m), s, M
.
.
.
.
.
.
.
.
.
.
.
.
(where M
= M0 +{m true})
In all the intervening states (until the rst 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 nal state
whichever got the lock rst. In general, that wont be the case.
Slide 226
Using Several Mutexes
lock m can block (thats the point). Hence, you can deadlock.
e = (lock m
1
; lock m
2
; l
1
:= !l
2
; unlock m
1
; unlock m
2
)
(lock m
2
; lock m
1
; l
2
:= !l
1
; unlock m
1
; unlock m
2
)
Slide 227
Locking Disciplines
So, suppose we have several programs e
1
, ..., e
k
, all well-typed with
e
i
:unit, that we want to execute concurrently without interference
(whatever that is). Think of them as transaction bodies.
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.
100
Slide 228
An Ordered 2PL Discipline, Informally
Fix an association between locations and mutexes. For simplicity, make it
1:1 associate l with m, l
1
with m
1
, etc.
Fix a lock acquisition order. For simplicity, make it m, m
0
, m
1
, m
2
, ....
Require that each e
i
acquires the lock m
j
for each location l
j
it uses, before it uses it
acquires and releases each lock in a properly-bracketed way
does not acquire any lock after its released any lock (two-phase)
acquires locks in increasing order
Then, informally, (e
1
... e
k
) should (a) never deadlock, and (b) be
serializable any execution of it should be equivalent to an execution of
e
(1)
; ...; e
(k)
for some permutation .
These are semantic properties again. In general, it wont be computable whether they hold.
For simple e
i
, though, its often obvious. Further, one can construct syntactic disciplines
that are checkable and are sucient to guarantee these.
Slide 229
Problem: Need a Thread-Local Semantics
Our existing semantics denes the behaviour only of global congurations
e, s, M). To state properties of subexpressions, e.g.
e
i
acquires the lock m
j
for each location l
j
it uses, before it uses it
which really means
in any execution of (e
1
... e
i
... e
k
), s, M), e
i
acquires the lock
m
j
for each location l
j
it uses, before it uses it
we need some notion of the behaviour of the thread e
i
on its own
Slide 230
Solution: Thread local semantics
Instead of only dening the global e, s, M) e
, s
, M
), with rules
(assign1) := n, s, M skip, s +{ n}, M if dom(s)
(parallel1)
e1, s, M e
1
, s
, M
e1 e2, s, M e
1
e2, s
, M
dene a per-thread e
a
e
, s
, M
1
e1 e2
a
e
1
e2
(c-assign)
e
:=n
e
dom(s)
e, s, M e
, s +{ n}, M
101
Slide 231
Note the per-thread rules dont mention s or M. Instead, we record in the
label a what interactions with the store or mutexes it has.
a ::= [ := n [ ! = n [ lock m [ unlock m
Conventionally, (tau), stands for no interactions, so e
e
if e does
an internal step, not involving the store or mutexes.
Theorem 29 (Coincidence of global and thread-local semantics) The
two denitions of agree exactly.
Proof strategy: a couple of rule inductions.
The full thread local semantics are on the next page.
Slide 232
Example of Thread-local transitions
For e = (lock m; (l := 1+!l ; unlock m)) we have
e
lock m
skip; (l := 1+!l ; unlock m)
(l := 1+!l ; unlock m)
!l =n
(l := 1 + n; unlock m) for any n Z
(l := n
; unlock m) for n
= 1 + n
l :=n
skip; unlock m
unlock m
unlock m
skip
Hence, using (t-parallel) and the (c-*) rules, for s
= s +l 1 +s(l ),
e e
, s, M
0
) skip e
, s
, M
0
)
(need l dom(s) also)
One often uses similar labelled transitions in dening communication between threads (or
machines), and also in working with observational equivalences for concurrent languages (cf.
bisimulation) to come in Topics in Concurrency.
102
Global Semantics Thread-Local Semantics
(op +) n
1
+ n
2
, s, M n, s, M if n = n
1
+ n
2
(op ) n
1
n
2
, s, M b, s, M if b = (n
1
n
2
)
(op1)
e
1
, s, M e
1
, s
, M
e
1
op e
2
, s, M e
1
op e
2
, s
, M
(op2)
e
2
, s, M e
2
, s
, M
v op e
2
, s, M v op e
2
, s
, M
, s
, M
:= e, s, M := e
, s
, M
(seq1) skip; e
2
, s, M e
2
, s, M
(seq2)
e
1
, s, M e
1
, s
, M
e
1
; e
2
, s, M e
1
; e
2
, s
, M
1
, s
, M
if e
1
then e
2
else e
3
, s, M if e
1
then e
2
else e
3
, s
, M
(while)
while e
1
do e
2
, s, M if e
1
then (e
2
; while e
1
do e
2
) else skip,
(parallel1)
e
1
, s, M e
1
, s
, M
e
1
e
2
, s, M e
1
e
2
, s
, M
(parallel2)
e
2
, s, M e
2
, s
, M
e
1
e
2
, s, M e
1
e
2
, s
, M
n if n = n
1
+ n
2
(t-op ) n
1
n
2
b if b = (n
1
n
2
)
(t-op1)
e
1
a
e
1
e
1
op e
2
a
e
1
op e
2
(t-op2)
e
2
a
e
2
v op e
2
a
v op e
2
(t-deref) !
!=n
n
(t-assign1) := n
:=n
skip
(t-assign2)
e
a
e
:= e
a
:= e
(t-seq1) skip; e
2
e
2
(t-seq2)
e
1
a
e
1
e
1
; e
2
a
e
1
; e
2
(t-if1) if true then e
2
else e
3
e
2
(t-if2) if false then e
2
else e
3
e
3
(t-if3)
e
1
a
e
1
if e
1
then e
2
else e
3
a
if e
1
then e
2
else e
3
(t-while)
while e
1
do e
2
if e
1
then (e
2
; while e
1
do e
2
) else skip
(t-parallel1)
e
1
a
e
1
e
1
e
2
a
e
1
e
2
(t-parallel2)
e
2
a
e
2
e
1
e
2
a
e
1
e
2
(t-lock) lock m
lock m
()
(t-unlock) unlock m
unlock m
()
(c-tau)
e
e
e, s, M e
, s, M
(c-assign)
e
:=n
e
dom(s)
e, s, M e
, s +{ n}, M
(c-lock)
e
lock m
e
M(m)
e, s, M e
, s, M +{m true}
(c-deref)
e
!=n
e
dom(s) s() = n
e, s, M e
, s, M
(c-unlock)
e
unlock m
e
e, s, M e
, s, M +{m false}
103
Slide 233
Now can make the Ordered 2PL Discipline precise
Say e obeys the discipline if for any (nite or innite)
e
a1
e
1
a2
e
2
a3
...
if a
i
is (l
j
:= n) or (!l
j
= n) then for some k < i we have
a
k
= lock m
j
without an intervening unlock m
j
.
for each j , the subsequence of a
1
, a
2
, ... with labels lock m
j
and
unlock m
j
is a prex of ((lock m
j
)(unlock m
j
))
. Moreover, if
(e
k
a
) then the subsequence does not end in a lock m
j
.
if a
i
= lock m
j
and a
i
= unlock m
j
then i < i
if a
i
= lock m
j
and a
i
= lock m
j
and i < i
then j < j
Slide 234
... and make the guaranteed properties precise
Say e
1
, ..., e
k
are serializable if for any initial store s, if
(e
1
... e
k
), s, M
0
)
, s
, M
, s
, M
).
Say they are deadlock-free if for any initial store s, if
(e
1
... e
k
), s, M
0
)
, s
, M) ,then not e
lock m
e
,
i.e.e