We will now define a small formal system for deriving typing relations
such as in
.
To this end we have in mind the following two classes of expression.
A type expression has the form of a type variable
(an
example of an atomic type) or the form
, where
and
are type expressions.
If we omit parentheses then arrow associates to the right; thus
is
.
An object expression has the form of a variable,
, an
abstraction,
or of an application,
, where a and b are
object expressions.
We say that b is the body
of
and the scope of
, a
binding operator.
In general, a variable y is bound in a term t if t has a subterm of
the form .
Any occurrence of y in b is bound.
A variable occurrence in t which is not bound is called free .
We say that a term a is free for a variable x in a term t
as long as no
free variable of a becomes bound when a is substituted for each free
occurrence of x. For example, z is free for x in
, but y is not.
If a has a free variable which becomes bound as a result of a substitution
then we say that the
variable has been captured .
Thus
``captures'' y if we try to substitute y for x in
.
If t is a term then
denotes the term which results from replacing
each free occurrence of x in t by a,
provided that a is free for x in t.
If a is not free for x then
denotes t with a replacing each
free x and the bound variables of t which would capture free variables of
a being renamed to prevent capture.
denotes the simultaneous substitution of for
.
We agree that two terms which differ only in their bound variable
names will be treated as equal everywhere in the theory, so
will denote the same term inside the theory regardless of
capture.
Thus, for example,
=
and
=
and
=
.
When we write
we mean that
names a
function whose type is
.
To be more explicit about the role of A, namely that it
is a type variable,
we declare A in a context or environment.
The environment has the single declaration
, which is read
``A is a type.''
For T a type expression and t an object expression,
will be called
a typing . To separate the context from the typing we use >>.
To continue the example above,
the full expression of our goal is
.
In general we use the following terminology.
Declarations are either type declarations, in which
case they have the form , or object declarations, in
which case they have the form
for T a type expression.
A hypothesis list has the form of a sequence of declarations; thus,
for instance,
is a hypothesis list.
In a proper hypothesis list the types referenced in object declarations
are declared first (i.e., to the left of the object declaration).
A typing
has the form
, where t is an object expression and
T is a type expression.
A goal has the form
,
where H is a hypothesis list and
is a typing.
We will now give rules for proving goals. The rules specify a finite number of subgoals needed to achieve the goal. The rules are stated in a ``top--down'' form or, following Bates [Bates 79], refinement rules. The general shape of a refinement rule is:
goal by![]()
- 1.
- subgoal 1
- .
- .
- .
- n.
- subgoal n.
Here is a sample rule.
It reads as follows: to prove that is a function in
in the
context H (or from hypothesis list H) we must achieve the subgoals
and
. That is, we must
show that the body
of the function has the type T under the assumption that the free variable
in the body has type S (a proof of this will demonstrate that T is a
type expression), and that S is a type expression.
A proof is a finite tree whose nodes are pairs consisting of a subgoal and a rule name or a placeholder for a rule name. The subgoal part of a child is determined by the rule name of the parent. The leaves of a tree have rule parts that generate no subgoals or have placeholders instead of rule names. A tree in which there are no placeholders is complete . We will use the term proof to refer to both complete and incomplete proofs.
Figure: Rules for the Typed Lambda Calculus
Figure gives the rules for the small theory.
Note that in rule (3) the square brackets indicate an optional part of
the rule name; if the new y part is missing then the
variable x is used, so that subgoal 1 is
The ``new variable'' part of a rule name allows the user to rename variables so as to prevent capture.
We say that an initial goal has the form
where the,
Figure describes a complete proof of a simple fact.
This proof provides simultaneously a derivation of
,
showing that
is a type expression; a derivation of
, showing that this is an
object expression; and type information about all
of the subterms, e.g.,
There is a certain conceptual economy in providing all of this information in one format, but the price is that some of the information is repeated unnecessarily. For example, we show thatis in
;
is in
given
;
is in
;
is in
.
Figure: A Sample Proof in the Small Type Theory
It is noteworthy that from a complete proof from an initial
goal of the form we know that t is a closed object
expression (one with no free variables) and T is a type expression whose
free variables are declared in H.
Also, in all hypotheses lists in all subgoals any expression appearing
on the right side of a declaration is either
or a type
expression whose variables are declared to the left.
Moreover, all
free variables of the conclusion in any subgoal are declared exactly
once in the corresponding hypothesis list.
In fact, no variable is declared at a subgoal unless it is free
in the conclusion.
Furthermore, every subterm
receives a type in
a subproof
, and in an application ,
, f will
receive a type
and a will receive the type
.
Properties of this variety can be proved by induction on
the construction of a complete proof.
For full Nuprl
many properties like these are proved in the
Ph.D. theses of R. W. Harper [Harper 85] and S. F. Allen [Allen 86].