Professional Documents
Culture Documents
4 Undenability
showing that it is denable in /nat is to observe that A(m + 1, n) it-
erates n times the function A(m, ), starting with A(m, 1). As an auxiliary,
let us dene the higher-order function
it : (nat nat) nat nat nat
to be the -abstraction
(f :nat nat)
(x:nat) s(e
d
(x))
of type nat nat. We then have
e
D
(e
D
) s(e
d
(e
D
))
s(e
D
(e
D
)).
But the termination theoremimplies that there exists n such that e
D
(e
D
)
n, and hence we have n s(n), which is impossible.
We say that a language / is universal if it is possible to write an inter-
preter for / in / itself. It is intuitively evident that f
univ
is computable in
the sense that we can dene it in a sufciently powerful programming lan-
guage. But the preceding argument shows that /nat is not sufciently
powerful for this task. That is, /nat is not universal. By demanding
termination we sacrice expressiveness. The preceding argument shows
that this is an inescapable tradeoff. If you want universality, you have to
give up termination, and if you want termination, then you must give up
universality. There is no alternative.
9.5 Notes
/nat was introduced by G odel in his study of the consistency of arith-
metic (G odel, 1980). G odel showed how to compile proofs in arithmetic
into well-typed terms of the language /nat , and to reduce the consis-
tency problem for arithmetic to the termination of programs in /nat .
This was perhaps the rst programming language whose design was di-
rectly inuenced by the verication (of termination) of its programs.
VERSION 1.30 REVISED 03.15.2012
Chapter 10
Plotkins PCF
The language /nat , also known as Plotkins PCF, integrates functions
and natural numbers using general recursion, a means of dening self-referential
expressions. In contrast to /nat expressions in /nat might not
terminate when evaluated: its denable functions are, in general, partial
rather than total. Informally, the difference between /nat and /nat
is that the former moves the proof of termination for an expression from
the expression itself into the mind of the programmer. The type system no
longer ensures termination, which permits a wider range of functions to be
dened in the system, but at the cost of admitting innite loops when the
termination proof is either incorrect or absent.
The crucial concept embodied in /nat is the xed point characteri-
zation of recursive denitions. In ordinary mathematical practice one may
dene a function f by recursion equations such as these:
f (0) 1
f (n + 1) (n + 1) f (n).
These may be viewed as simultaneous equations in the variable, f , ranging
over functions on the natural numbers. The function we seek is a solution to
these equationsa function f : N Nsuch that the above conditions are
satised. We must, of course, show that these equations have a unique so-
lution, which is easily shown by mathematical induction on the argument
to f .
The solution to such a system of equations may be characterized as
the xed point of an associated functional (operator mapping functions to
86
functions). To see this, let us re-write these equations in another form:
f (n)
1 if n = 0
n f (n
/
) if n = n
/
+ 1.
Re-writing yet again, we seek f given by
n
1 if n = 0
n f (n
/
) if n = n
/
+ 1.
Now dene the functional F by the equation F( f ) = f
/
, where f
/
is given by
n
1 if n = 0
n f (n
/
) if n = n
/
+ 1.
Note well that the condition on f
/
is expressed in terms of the argument, f ,
to the functional F, and not in terms of f
/
itself! The function f we seek is
then a xed point of F, which is a function f : N Nsuch that f = F( f ). In
other words f is dened to the x(F), where x is an operator on functionals
yielding a xed point of F.
Why does an operator such as F have a xed point? Informally, a
xed point may be obtained as the limit of a series of approximations of
the desired solution obtained by iterating the functional F. This is where
partial functions come into the picture. Let us say that a partial func-
tion, on the natural numbers, is an approximation to a total function, f ,
if (m) = n implies that f (m) = n. Let : N N be the totally unde-
ned partial function(n) is undened for every n N. Intuitively, this
is the worst approximation to the desired solution, f , of the recursion
equations given above. Given any approximation, , of f , we may im-
prove it by considering
/
= F(). Intuitively,
/
is dened on 0 and on
m + 1 for every m 0 on which is dened. Continuing in this manner,
//
= F(
/
) = F(F()) is an improvement on
/
, and hence a further im-
provement on . If we start with as the initial approximation to f , then
pass to the limit
lim
i0
F
(i)
(),
we will obtain the least approximation to f that is dened for every m N,
and hence is the function f itself. Turning this around, if the limit exists, it
must be the solution we seek.
This xed point characterization of recursion equations is taken as a
primitive concept in /nat we may obtain the least xed point of any
VERSION 1.30 REVISED 03.15.2012
10.1 Statics 87
functional denable in the language. Using this we may solve any set of
recursion equations we like, with the proviso that there is no guarantee
that the solution is a total function. Rather, it is guaranteed to be a partial
function that may be undened on some, all, or no inputs. This is the price
we pay for expressive powerwe may solve all systems of equations, but
the solution may not be as well-behaved as we might like. It is our task as
programmers to ensure that the functions dened by recursion are total
all of our loops terminate.
10.1 Statics
The abstract binding syntax of /nat is given by the following gram-
mar:
Typ ::= nat nat naturals
parr(
1
;
2
)
1
2
partial function
Exp e ::= x x variable
z z zero
s(e) s(e) successor
ifz(e; e
0
; x.e
1
) ifz e z e
0
| s(x) e
1
zero test
lam[](x.e)
(x:) e abstraction
ap(e
1
; e
2
) e
1
(e
2
) application
fix[](x.e) fix x: is e recursion
The expression fix[](x.e) is called general recursion; it is discussed in
more detail below. The expression ifz(e; e
0
; x.e
1
) branches according to
whether e evaluates to z or not, binding the predecessor to x in the case
that it is not.
The statics of /nat is inductively dened by the following rules:
, x : x :
(10.1a)
z : nat
(10.1b)
e : nat
s(e) : nat
(10.1c)
e : nat e
0
: , x : nat e
1
:
ifz(e; e
0
; x.e
1
) :
(10.1d)
REVISED 03.15.2012 VERSION 1.30
88 10.2 Dynamics
, x :
1
e :
2
lam[
1
](x.e) : parr(
1
;
2
)
(10.1e)
e
1
: parr(
2
; ) e
2
:
2
ap(e
1
; e
2
) :
(10.1f)
, x : e :
fix[](x.e) :
(10.1g)
Rule (10.1g) reects the self-referential nature of general recursion. To show
that fix[](x.e) has type , we assume that it is the case by assigning that
type to the variable, x, which stands for the recursive expression itself, and
checking that the body, e, has type under this very assumption.
The structural rules, including in particular substitution, are admissible
for the static semantics.
Lemma 10.1. If , x : e
/
:
/
, e : , then [e/x]e
/
:
/
.
10.2 Dynamics
The dynamic semantics of /nat is dened by the judgements e val,
specifying the closed values, and e e
/
, specifying the steps of evaluation.
The judgement e val is dened by the following rules:
z val
(10.2a)
[e val]
s(e) val
(10.2b)
lam[](x.e) val
(10.2c)
The bracketed premise on Rule (10.2b) is to be included for the eager inter-
pretation of the sucessor operation, and omitted for the lazy interpretation.
(See Chapter 37 for a further discussion of laziness.)
The transition judgement e e
/
is dened by the following rules:
e e
/
s(e) s(e
/
)
(10.3a)
e e
/
ifz(e; e
0
; x.e
1
) ifz(e
/
; e
0
; x.e
1
)
(10.3b)
ifz(z; e
0
; x.e
1
) e
0
(10.3c)
VERSION 1.30 REVISED 03.15.2012
10.2 Dynamics 89
s(e) val
ifz(s(e); e
0
; x.e
1
) [e/x]e
1
(10.3d)
e
1
e
/
1
ap(e
1
; e
2
) ap(e
/
1
; e
2
)
(10.3e)
e
1
val e
2
e
/
2
ap(e
1
; e
2
) ap(e
1
; e
/
2
)
(10.3f)
[e val]
ap(lam[](x.e); e
2
) [e
2
/x]e
(10.3g)
fix[](x.e) [fix[](x.e)/x]e
(10.3h)
The bracketed Rule (10.3a) is to be included for an eager interpretation of
the successor, and omitted otherwise. Bracketed Rule (10.3f) and the brack-
eted premise on Rule (10.3g) are to be included for a call-by-value interpre-
tation, and omitted for a call-by-name interpretation, of function applica-
tion. Rule (10.3h) implements self-reference by substituting the recursive
expression itself for the variable x in its body; this is called unwinding the
recursion.
Theorem 10.2 (Safety). 1. If e : and e e
/
, then e
/
: .
2. If e : , then either e val or there exists e
/
such that e e
/
.
Proof. The proof of preservation is by induction on the derivation of the
transition judgement. Consider Rule (10.3h). Suppose that fix[](x.e) :
. By inversion and substitution we have [fix[](x.e)/x]e : , as re-
quired. The proof of progress proceeds by induction on the derivation of
the typing judgement. For example, for Rule (10.1g) the result follows im-
mediately since we may make progress by unwinding the recursion.
It is easy to check directly that if e val, then e is irreducible in that there
is no e
/
such that e e
/
. The safety theorem implies the converse, namely
that an irreducible expression is a value, provided that it is closed and well-
typed.
Denitional equality for the call-by-name variant of /nat , written
e
1
e
2
: , is dened to be the strongest congruence containing the
following axioms:
ifz(z; e
0
; x.e
1
) e
0
:
(10.4a)
REVISED 03.15.2012 VERSION 1.30
90 10.3 Denability
ifz(s(e); e
0
; x.e
1
) [e/x]e
1
:
(10.4b)
fix[](x.e) [fix[](x.e)/x]e :
(10.4c)
ap(lam[
1
](x.e
2
); e
1
) [e
1
/x]e
2
:
(10.4d)
These rules are sufcient to calculate the value of any closed expression of
type nat: if e : nat, then e n : nat iff e
n.
10.3 Denability
General recursion is a very exible programming technique that permits a
wide variety of functions to be dened within /nat . The drawback
is that, in contrast to primitive recursion, the termination of a recursively
dened function is not intrinsic to the program itself, but rather must be
proved extrinsically by the programmer. The benet is a much greater free-
dom in writing programs.
Let us write fun x(y:
1
):
2
is e for a recursive function within whose
body, e :
2
, are bound two variables, y :
1
standing for the argument and
x :
1
2
standing for the function itself. The dynamic semantics of this
construct is given by the axiom
(fun x(y:
1
):
2
is e)(e
1
) [fun x(y:
1
):
2
is e, e
1
/x, y]e
.
That is, to apply a recursive function, we substitute the recursive function
itself for x and the argument for y in its body.
Recursive functions may be dened in /nat using a combination
of recursion and functions, writing
fix x:
1
2
is
(y:
1
) e
for fun x(y:
1
):
2
is e. It is a good exercise to check that the static and
dynamic semantics of recursive functions are derivable fromthis denition.
The primitive recursion construct of /nat is dened in /nat
using recursive functions by taking the expression
rec e z e
0
| s(x) with y e
1
: nat nat
such that (m) = n iff e
univ
(e)(m) = n iff e(m) n : nat.
1
See Chapter 17 for further discussion of Churchs Law.
REVISED 03.15.2012 VERSION 1.30
92 10.4 Notes
In contrast to /nat , the universal function
univ
for /nat is par-
tial (may be undened for some inputs). It is, in essence, an interpreter
that, given the code e of a closed expression of type nat nat, simulates
the dynamic semantics to calculate the result, if any, of applying it to the m,
obtaining n. Since this process may fail to terminate, the universal function
is not dened for all inputs.
By Churchs Law the universal function is denable in /nat . In
contrast, we proved in Chapter 9 that the analogous function is not den-
able in /nat using the technique of diagonalization. It is instructive
to examine why that argument does not apply in the present setting. As in
Section 9.4, we may derive the equivalence
e
D
(e
D
) s(e
D
(e
D
))
for /nat . The difference, however, is that this equation is not incon-
sistent! Rather than being contradictory, it is merely a proof that the expres-
sion e
D
(e
D
) does not terminate when evaluated, for if it did, the result
would be a number equal to its own successor, which is impossible.
10.4 Notes
The language /nat is derived from Plotkin (1977). Plotkin introduced
PCF to study the relationship between its operational and denotational se-
mantics, but many authors have used PCF as the subject of study for many
issues in the design and semantics of languages. In this respect PCF may
be thought of as the E. coli of programming languages.
VERSION 1.30 REVISED 03.15.2012
Part IV
Finite Data Types
Chapter 11
Product Types
The binary product of two types consists of ordered pairs of values, one from
each type in the order specied. The associated eliminatory forms are pro-
jections, which select the rst and second component of a pair. The nullary
product, or unit, type consists solely of the unique null tuple of no val-
ues, and has no associated eliminatory form. The product type admits both
a lazy and an eager dynamics. According to the lazy dynamics, a pair is
a value without regard to whether its components are values; they are not
evaluated until (if ever) they are accessed and used in another computation.
According to the eager dynamics, a pair is a value only if its components
are values; they are evaluated when the pair is created.
More generally, we may consider the nite product,
i
iI
, indexed by a
nite set of indices, I. The elements of the nite product type are I-indexed
tuples whose ith component is an element of the type
i
, for each i I.
The components are accessed by I-indexed projection operations, general-
izing the binary case. Special cases of the nite product include n-tuples,
indexed by sets of the form I = 0, . . . , n 1 , and labeled tuples, or records,
indexed by nite sets of symbols. Similarly to binary products, nite prod-
ucts admit both an eager and a lazy interpretation.
96 11.1 Nullary and Binary Products
11.1 Nullary and Binary Products
The abstract syntax of products is given by the following grammar:
Typ ::= unit unit nullary product
prod(
1
;
2
)
1
2
binary product
Exp e ::= triv null tuple
pair(e
1
; e
2
) e
1
, e
2
ordered pair
pr[l](e) e l left projection
pr[r](e) e r right projection
There is no elimination formfor the unit type, there being nothing to extract
from the null tuple.
The statics of product types is given by the following rules.
: unit
(11.1a)
e
1
:
1
e
2
:
2
e
1
, e
2
:
1
2
(11.1b)
e :
1
2
e l :
1
(11.1c)
e :
1
2
e r :
2
(11.1d)
The dynamics of product types is specied by the following rules:
val
(11.2a)
[e
1
val] [e
2
val]
e
1
, e
2
val
(11.2b)
e
1
e
/
1
e
1
, e
2
e
/
1
, e
2
(11.2c)
e
1
val e
2
e
/
2
e
1
, e
2
e
1
, e
/
2
(11.2d)
e e
/
e l e
/
l
(11.2e)
e e
/
e r e
/
r
(11.2f)
VERSION 1.30 REVISED 03.15.2012
11.2 Finite Products 97
[e
1
val] [e
2
val]
e
1
, e
2
l e
1
(11.2g)
[e
1
val] [e
2
val]
e
1
, e
2
r e
2
(11.2h)
The bracketed rules and premises are to be omitted for a lazy dynamics,
and included for an eager dynamics of pairing.
The safety theorem applies to both the eager and the lazy dynamics,
with the proof proceeding along similar lines in each case.
Theorem 11.1 (Safety). 1. If e : and e e
/
, then e
/
: .
2. If e : then either e val or there exists e
/
such that e e
/
.
Proof. Preservation is proved by induction on transition dened by Rules (11.2).
Progress is proved by induction on typing dened by Rules (11.1).
11.2 Finite Products
The syntax of nite product types is given by the following grammar:
Typ ::= prod(i
i
iI
)
i
iI
product
Exp e ::= tpl(i e
i
iI
) e
i
iI
tuple
pr[i](e) e i projection
The variable I stands for a nite index set over which products are formed.
The type prod(i
i
iI
), or
iI
i
for short, is the type of I-tuples of
expressions e
i
of type
i
, one for each i I. An I-tuple has the form
tpl(i e
i
iI
), or e
i
iI
for short, and for each i I the ith projection
from an I-tuple, e, is written pr[i](e), or e i for short.
When I = i
1
, . . . , i
n
, the I-tuple type may be written in the form
i
1
1
, . . . , i
n