Mitch Plotkin 88 PDF
Mitch Plotkin 88 PDF
JOHN C. MITCHELL
Stanford University
AND
GORDON D. PLOTKIN
University of Edinburgh
Abstract data type declarations appear in typed programming languages like Ada, Alphard, CLU and
ML. This form of declaration binds a list of identifiers to a type with associated operations, a
composite “value” we call a data algebra. We use a second-order typed lambda calculus SOL to show
how data algebras may be given types, passed as parameters, and returned as results of function calls.
In the process, we discuss the semantics of abstract data type declarations and review a connection
between typed programming languages and constructive logic.
Categories and Subject Descriptors: D.3 [Software]: Programming Languages; D.3.2 [Program-
ming Languages]: Language Classifications-applicative languages; D.3.3 [Programming Lan-
guages]: Language Constructs--abstract data types; F.3 [Theory of Conmputation]: Logics and
Meanings of Programs; F.3.2 [Logics and Meanings of Programs]: Semantics of Programming
Languages-denotational semantics, operational semantics; F.3.3 [Logics and Meanings of Pro-
grams]: Studies of Program Constructs-type structure
General Terms: Languages, Theory, Verification
Additional Key Words and Phrases: Abstract data types, lambda calculus, polymorphism, program-
ming languages, types
1. INTRODUCTION
Ada packages [17], Alphard forms [66, 711, CLU clusters [41, 421, and abstype
declarations in ML [23] all bind identifiers to values. Although there are minor
variations among these constructs, each allows a list of names to be bound to a
composite value consisting of “private” type and one or more operations. For
example, the ML declaration
abstype complex = real # real
with create = . . .
and pius = . . .
and re = . . .
andim= ..-
An earlier version of this paper appeared in the Proceedings of the 22thACM Symposium on Principles
of Programming Languages (New Orleans, La., Jan. 14-16). ACM, New York, 1985.
Authors’ addresses: J. C. Mitchell, Department of Computer Science, Stanford University, Stanford,
CA 94305; G. D. Plotkin, Department of Computer Science, University of Edinburgh, Edinburgh,
Scotland EH9 352.
Permission to copy without fee all or part of this material is granted provided that the copies are not
made or distributed for direct commercial advantage, the ACM copyright notice and the title of the
publication and its date appear, and notice is given that copying is by permission of the Association
for Computing Machinery. To copy otherwise, or to republish, requires a fee and/or specific
permission.
0 1988 ACM 0164-0925/88/0700-0470 $01.50
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988, Pages 470-502.
Abstract Types Have Existential Type l 471
binds the identifiers complex, create, plus, re, and im to the components of an
implementation of complex numbers. The implementation consists of the collec-
tion defined by the ML expression real # real, meaning the type of pairs of
reals, and the functions denoted by the code for create, plus, and so on. An
important aspect of this construct is that access to the representation is limited.
We cannot apply arbitrary operations on pairs of reals to elements of type
complex; only the explicitly declared operations may be used.
We will call a composite value constructed from a set and one or more
operations, packaged up in a way that limits access, a data algebra. We will
discuss the typing rules associated with the formation and the use of data algebras
and observe that data algebras themselves may be given types in a straightforward
manner. This will allow us to devise a typed programming notation in which
implementations of abstract data types may be passed as parameters or returned
as the results of function calls.
The phrase “abstract data type” sometimes refers to a class of algebras (or
perhaps an initial algebra) satisfying some specification. For example, the ab-
stract type stack is sometimes regarded as the class of all algebras satisfying the
familiar logical formulas axiomatizing push and pop. Associated with this view is
the tenet that a program must rely only on the data type specification, as opposed
to properties of a particular implementation. Although this is a valuable guiding
principle, most programming languages do not contain assertions or their proofs,
and without this information it is impossible for a compiler to guarantee that a
program depends only on a data type specification. Since we are primarily
concerned with properties of the abstract data type declarations used in common
programming languages, we will focus on the limited form of information hiding
or “abstraction” provided by conventional type checking rules.
We can be more specific about how data algebras are defined by considering
the declaration of complex numbers in more detail. Using an explicitly typed
ML-like notation, the declaration sketched earlier looks something like this:
abstype complex = real # real
with create: real + real + complex = Xx: real. Xy: real. ( X, y )
and plus: complex --, complex =
Xz:real # real. Xw:real # real. ( fst(z) + fst(w), snd(z) + snd(w))
and re: complex + real = Xz:real # real.fst(z)
and im: complex + real = Xz:real # real. snd(z)
The identifiers complex, create, plus, re, and im are bound to a data algebra whose
elements are represented as pairs of reals, as specified by the type expression
real # real. The operations of the data algebra are given by the function expres-
sions to the right of the equals signs1 Notice that the declared types of the
operations differ from the types of the implementing functions. For example, re
is declared to have type complex + real, but the implementing expression has
type real # real + real. This is because operations are defined using the concrete
representation of values, but the representation is hidden outside the declaration.
In the next section, we will discuss the type checking rules associated with
abstract data type declarations, which are designed to make complex numbers
1 In most programming languages, function definitions have the form “create(x:real, y:real) = . . .”
In the example above, we have used explicit lambda abstraction to move the formal parameters from
the left- to the right-hand sides of the equals signs.
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
472 9 J. C. Mitchell and G. D. Plotkin
“abstract” outside the data algebra definition. In the process, we will give types
to data algebras. These will be existential types, which were originally developed
in constructive logic and are closely related to infinite sums (as in category
theory, for example). In Section 3, we describe a statically typed language SOL.
This language is a notational variant of Girard’s system F, developed in the
analysis of constructive logic [21,22], and an extension of Reynolds’ polymorphic
lambda calculus [62]. An operational semantics of SOL, based on the work of
Girard and Reynolds, is presented using reduction rules. However, we do not
address a variety of practical implementation issues. Although the basic calculus
we use has been known for some time, we believe that the analysis of data
abstraction using existential types originates with this paper. (A preliminary
version appeared as [56].)
The use of SOL as a proof-theoretic tool is based on an analogy between types
and constructive logic. This analogy gives rise to a large family of typed languages
and suggests that our analysis of abstract data types applies to more expressive
languages involving specifications. Since the connection between constructive
proofs and typed programs does not seem to be well known in the programming
language community (at least at present), our brief discussion of specifications
will follow a review of the general analogy in Section 4. Additional SOL program-
ming examples are given in Section 5.
The design of SOL suggests new programming languages along the lines of
Ada, Alphard, CLU, and ML but with richer and more flexible type structures.
In addition, SOL seems to be a natural “kernel language” for studying the
semantics of languages with polymorphic functions and abstract data type
declarations. For this reason, we expect SOL to be useful in future studies of
current languages. It is clear that SOL provides greater flexibility in the use of
abstract data types than previous languages, since data algebras may be passed
as parameters and returned as results. We believe that this is accomplished
without any compromise in “type security.” However, since we do not have a
precise characterization of type security, we are unable to show rigorously that
SOL is secure.’
Some languages that are similar to SOL in scope and intent are Pebble [7],
designed to capture some essential features of Cedar (an extension of Mesa [57]),
and Kernel Russell, KR, of [28], based on Russell [14, 15, 161. Martin-Lof’s
constructive type theory [46] and the calculus of constructions [ll] are farther
from programming language syntax but share many properties of SOL. Some
features of Martin-Lof’s system have been incorporated into the Standard ML
module design [44, 541, which was formulated after the work described here was
completed. We will compare SOL with some of these languages in Section 3.8.
’ Research begun after this paper was written has shed some light on the type security of SOL. See
[52] and [55] for further discussion.
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type 473
common languages, there is one novel aspect that leads to additional flexibility:
We separate the names bound by a declaration from the data algebra they come
to denote. For example, the complex number example is written as follows:
abstype complex with
create: real --, real * complex,
plus: complex + complex,
re: complex + real,
im: complex + real
is
pack real A real
Xx: real.Xy:real. (1c,y )
Xz:real A real.hw:real A real.(fst(z) + fst(lo), snd(z) + snd(w))
Xzreal A real.fst(z)
Xz:real A real.snd(z) to 3 t.[ (real + real --$ t) A (t + t) A (t + real) A (t + real)],
where the expression beginning pack and running to the end of the example is
considered to be the definition of the data algebra. (In SOL, we write real A real
for the type of pairs of reals. When parentheses are omitted, the connective A
has higher precedence than +-.) This syntax is designed to allow implementations
of abstract data types (data algebras) to be defined using expressions of any form
and to emphasize the view that abstract data type declarations commonly
combine two separable actions, defining a data algebra and binding identifiers to
its components.
The SOL declaration of an abstract data type t with operations x1, . . . , X, has
the general form
abstype t with x1: u,, . . . , x,: CT,,is M in N,
where ul, . . . . a, are the types of the operations and M is a data algebra
expression. As in the complex number example above, the type identifier t often
appears in the types of operations x1, . . . , x,. The scope of the declaration is N.
The simplest data algebra expressions in SOL are those of the form
pack TM, . -. M,, to 3t.u
involving a basic data algebra expression only makes sense if k = n (so that each
operation gets an implementation) and the types of Ml, . . . , Mk match the
declared types of the operations x1, . . . , x, in some appropriate way. The matching
rule in SOL is that the type of Mi must be [T/t ]~i, the result of substituting T for
tin ui (with appropriate renaming of bound type variables in ui). To see how this
works in practice, look back at the complex number declaration. The declared
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
474 - J. C. Mitchell and G. D. Plotkin
type of the first operation create is real + real + complex, whereas the type of
the implementing function expression is real + real + (real A real). The matching
rule is satisfied in this case because the type of the implementing code may be
obtained by substituting real A real for complex in the declared type real + real
+ complex.
We can recast the matching rule using the existential types we have associated
with data algebra expressions. An appropriate type for a data algebra is an
expression that specifies how the operations may be used, without describing the
type used to represent values. If each Mi has type [~/t]ui, then we say that
pack 7M1 . . . M,,to 3t.al A . . . A u,
has type 3t.ul A . . . A un. This type may be read “there exists a type t with
operations of types u1 and . . . and bn.” The operator 3 binds the type variable t
in 3 t.a, so 3 t.u = 3 s.[s/t ]a when s does not occur in u. Existential types provide
just enough information to verify the matching condition stated above, without
providing any information about the representation of the carrier or the algo-
rithms used to implement the operations. The matching rule for abstype may
now be stated.
(AB.l) In abstype t with x1: ul, . . . , x,: a, is M in N, the data algebra
expression M must have type 3 t.ul A . - - A a,.
Although it may seem unnecessarily verbose to write the type of pack - . . to
. . . as part of the expression, this is needed to guarantee that the type is unique.
Without the type designation, an expression like pack TM could have many
types. For example, if the type of M is 7 + 7, then pack TM might have types
3 t.t -+ t, 3 t.t += 7, 3 t.7 -+ t, and 3 t.7 + 7. To avoid this, we have included the
intended type of the whole expression as part of the syntax. Something equivalent
to this is done in most other languages. In CLU, for example, types are determined
using the keyword cvt, which specifies which occurrences of the representation
type are to be viewed as abstract. ML, as documented in [23], uses keywords abs
and rep, whereas later versions [50] use type constructors and pattern matching.
An important constraint in abstract type declarations is that only the explicitly
declared operations may be applied to elements of the type [58]. In SOL, this
constraint is formulated as follows:
(AB.2) In abstype t with x1: ul,. . . , x ,, : u,, is M in N, if y is any free identifier
in N different from x1, . . . , x,, then t must not appear free in the type of y.
In addition to accomplishing the goals put forth in [58], this condition is easily
seen to be a natural scoping rule for type identifiers. We can see why (AB.2)
makes sense and what kind of expressions it prevents by considering the following
example.
let f = Xx: stack . . . in
abstype stack with empty : stack,
push : int A stack + stack,
pop : stack + int A stack
is . . .
in f (empty)
end
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988
Abstract Types Have Existential Type 475
and
pack pP, a-. P, to 3 t.o
is a data algebra expression of SOL with type 3 t.a. Conditional algebra expres-
sions are useful for selecting between several alternative implementations of the
same abstract type. For example, a program using matrices may choose between
sparse or dense matrix implementations using a conditional data algebra expres-
sion inside an abstype declaration. Without (AB.3), the type of an abstype
expression with a data algebra conditional such as
abstype t with x1: t, . . . , xn: u,
isifBthen(pack7M1 e-e M,toilt.(r)
else (pack pP1 . . . P, to 3 t.u)
in x1
may depend on whether the conditional test is true or false. (Specifically, the
meaning of the expression above is either iVll or PI, depending on B). Thus,
without (AB.3), we cannot type check expressions with conditional data algebra
expressions at “compile time,” that is, without computing the values of arbitrary
tests.
Another way of describing this situation is to consider the form of type
expression we would need if we wanted to give the expression above a type
without evaluating B. Since the type of the expression actually depends on the
value of B, we would have to mention B in the type. This approach is used in
some languages (notably Martin-Lof’s intuitionistic type theory), but it intro-
duces ordinary value expressions into types. Consequently, type equality depends
on equality of ordinary expressions. Some of the simplicity of SOL is due to the
separation of type expressions from “ordinary” expressions, and considerable
complication would arise from giving this up.
Finally, the termination of all recursion-free programs seems to fail if we drop
(AB.3). In other words, there is a roundabout way of writing programs that do
not halt on any input, without using any recursive declarations or iterative
constructs. This is a complex issue whose full explanation is beyond the scope of
this paper. The reader is referred to [lo], [29], [49], and [54] for further discussion.
Putting all of these reasons together, it seems that dropping (AB.3) would change
the nature of SOL quite drastically. Therefore, we leave the study of abstype
without (AB.3) to future research.
With rule (AB.3) in place, we can allow very general computation with data
algebras. In addition to conditional data algebra expressions, SOL allows data
algebra parameters. An example that illustrates their use is the general tree
search routine given in Section 2.5. The usual algorithms for depth-first search
and breadth-first search may be written so that they are virtually identical,
except that depth-first search uses a stack and breadth-first search uses a queue.
The general tree-search algorithm in Section 2.6 is based on this idea, using a
formal parameter in place of a stack or queue. If a stack data algebra is supplied
as an actual parameter, then the algorithm performs depth-first search. Similarly
a queue parameter produces breadth-first search. Additional structures like
priority queues may also be passed as actual parameters, resulting in “best-first”
search algorithms.
Data algebra parameters are allowed in SOL simply because the typing rules
do not prevent them. If z is a variable with type 3 t.al A . . . A IS,,,then
abstype t with x1: (Jo, . . . , x,: U, is z in N
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type 477
the typing rules using type assignments, which are functions from ordinary
variables to type expressions. For each type assignment A, we define a partial
function TypeA from expressions to types. Intuitively, TypeA (M) = u means that
the type of M is u, given the assignment A of types to variables that may appear
free in M. Each partial function Type* is defined by a set of deduction rules of
the form
TypeA = u, . . .
Typea = T
meaning that if the antecedents hold, then the value of TypeA at N is defined to
be 7. The conditions on TypeA may mention other type assignments if N
binds variables that occur in subterms.
A variable of any type is a term. Formally, we have the axiom
TyPeA = A(x)
saying that a variable x has whatever type it is given. We also allow term
constants, provided that each constant is assigned a type that does not contain
free type variables. One particularly useful constant is the polymorphic condi-
tional cond, which will be discussed after V-types are introduced.
‘I’nx~[,:,~(M) = 7
TypeA(Xx:a.M) = u+ 7
and
TypeA = (T + T, TypeA = u
TypeA = 7
Thus a typed lambda expression has a functional type and may be applied to any
argument of the correct type. An example function expression is the lambda
expression
Xx:int. x + 1
for the successor function on integers.
The semantics of SOL is described using a set of operational reduction rules.
The reduction rules use substitution, and, therefore, require the ability to rename
bound variables. For functions, we rename bound variables according to the
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1933.
Abstract Types Have Existential Type 479
equational axiom
xx : u.M = xy : u.[ y/x]M, y not free in M
The operational semantics of function definition and application are captured by
the reduction rule
(Xx : a.M)N + [N/x]M,
where we assume that substitution [N/x]M includes renaming of bound variables
to avoid capture. (Technically speaking, the collection of SOL reduction rules
defines a relation on equivalence classes of SOL terms, where equivalence is
defined by the collection of all SOL axioms for renaming bound variables. See,
e.g., [2] for further discussion). Intuitively, the reduction rule above says that the
expression (XX: a.M)N may be evaluated by substituting the argument N for
each free occurrence of the variable x in M. For example,
(XX: int. x + 2)5 + 5 + 2.
Some readers may recognize this mechanism as the “copy rule” of ALGOL 60.
We write +z+ for the congruent and transitive closure of +.
We introduce let declarations by the abbreviation
let x = M in N :: = (Xx : a.N)M,
where c = TypeA( Note that since the assignment A of types to variables is
determined by context, the definition of let depends on the context in which it
is used. An alternative would be to write let x: u = M in N, but since u is always
uniquely determined, the more succinct let notation seems preferable.
The typing rules and operational semantics for let are inherited directly
from X. For example, we have
let f = Xn:int. x + 3 in f(f(2)) * (2 + 3) + 3.
A similar declaration is the ML recursive declaration
letrec f = M in N
which declares f to be a recursive function with body M. (If f occurs in M, then
this refers recursively to the function being defined; occurrences of f in M are
bound by letrec.) Although we use letrec in programming examples, it is
technically useful to define pure SOL as a language without recursion. This pure
language has simpler theoretical properties, making it easier to study the type
structure of SOL.
The operational semantics of pairing and projection are given by the reduction
rules
fst(M, N) * M, snd(M, N) + N.
For example,
let p = (1, 2) in f&(p) + 1.
Note that the type of this case statement remains int if z is declared to be
inright of a Boolean instead of inleft of an integer.
3.3 Polymorphism
Intuitively, Xt.M is a polymorphic expression that can be “instantiated” to values
of various types. In an Ada-like syntax, the term Xt.M would be written
generic (type t )A4
Polymorphic expressions are instantiated using type application, which we will
write using braces 1,) to distinguish it from an ordinary function application.
If M has type V t.a, then the type of M(T ) is [7/t ]u. The Ada-like syntax for
M(T) is
new M(7).
Intuitive Semantics and Reduction Rules for Xt.M. The intuitive meaning of
ht.M is the infinite product of all meanings of A4 as t varies over all types. In the
next section, we see that abstract data type declarations involve infinite sums.
To see the similarity between V-types and infinite products, we review the
general notion of product, as ,used in category theory [l, 27, 431. There are two
parts to the definition: product types (corresponding to product objects in
categories) and product elements (corresponding to product arrows). Given a
collection S of types, the product type n S has the property that for each s E S
there is a projection function proj s from n S to s. Furthermore, given any
family F = ( fs] of elements indexed by S with fs E s, there is a unique product
element fl F with the property that
proj s fl F = fs.
Uniqueness of products means that if proj s n F = g, for all s E S, then
nF=flG.
The correspondence with SOL is that we can think of a type expression u and
type variable t as defining a collection of types, namely the collection S of all
substitution instances [T/t]u of CJ.If A4 is a term with t not free in the type of
any free ordinary variable, then M and t determine a collection of substitution
instances [T/t]M. It is easy to show that if t is not free in the type of any
free variable of M and TypeA = u, then TypeA ([T/t ]M) = [T/t ]u. By letting
f [T/t10
= [T/t JM, we may view the collection of substitution instances of M as a
family F = 1fs) indexed by elements of S. Using this indexing of instances, we
may regard V t.a as a product type n S and Xt.M as a product element JJ F, with
projection accomplished by type application. The product axiom above leads to
the reduction rule
(ht.M)bl * b/tlM
where we assume that bound variables are renamed in [r/t]M to avoid capture
of free type variables in 7. Since X binds type variables, we also have the renaming
rule
Xt.M = Xs.[s/t]M, s not free in Xt.M.
There is a third “extensionality” rule for X-abstraction over types, stemming
from the uniqueness of products, but we are not concerned with it in this paper
(primarily because it does not seem to be a part of ordinary programming language
implementation and because it complicates the Static Typing Theorem in
Section 3.7).
where
u= Ul A (*.. A un . ..).
Polymorphic data algebras may be written in Ada, Alphard, CLU, and ML.
Since SOL has X-binding of types, we can also write polymorphic representations
in SOL. For example, let t-stuck be a representation of stacks of elements of t,
say,
t-stuck ::= pack (int A array of t ) empty push pop
to 3s.~ A (t A s + s) A (s + t A s),
where empty represents the empty stack, and push and pop are functions
implementing the usual push and pop operations. Then the expression
stack ::= ht.t-stack
with type
stuck: Vt. 3s.[s A (t A s + s) A (s - t A s)]
is a polymorphic implementation of stacks. We could also define a polymorphic
implementation of queues
queue: Vt. 3q.[q A (t A q + q) A (q + t A q)]
similarly. Note that stuck and queue have the same existential type, reflecting
the fact that as algebras, they have the same signature.
Abstract data type declarations are formed according to the rule
declares a type of integer stacks with three operations. Note that the names for
the stack operations are local to N, rather than defined globally by stuck.
3.5 Programming with Data Algebras
One feature of SOL is that a program may select one of several data type
implementations at run time. For example, a parser that uses a symbol table
could be parameterized by the symbol table implementation and passed either a
hash table or binary tree implementation according to conditions. This ability to
manipulate data algebras makes a common feature of file systems and linkage
editors an explicit part of SOL. For example, many of the functions of the CLU
library, a design for handling multiple implementations [41], may be accom-
plished directly by programs.
In allowing programs to select representations, we also allow programs to
choose among data types that have the same signature. This flexibility accrues
from the fact that SOL types are signatures, rather than complete data type
specifications: Since we only check signature information, data types that have
the same signature have implementations of the same existential type. This is
used to advantage in the tree-search algorithm of Figure 1. It may also be argued
that this points out a deficiency in the SOL typing discipline. In a language with
specifications as types, type checking could guarantee that every actual parameter
to a function is an implementation of a stack, rather than just an implementation
with a designated element and two binary operations. Languages with this
capability will be discussed briefly in Section 4.4.
The common algorithm for depth-first search uses a stack, whereas the usual
approach to breadth-first search uses a queue. Since stack and queue implemen-
tations have the same SOL type, the program fragment in Figure 1 declares a
tree-search function with a data algebra parameter instead of a stack or queue.
If a stack is passed as a parameter, the function does depth-first search, while a
queue parameter produces breadth-first. In addition, other data algebras, such as
priority queues, could be passed as parameters. A priority queue produces a “best-
first” search; the search proceeds along paths that the priority queue deems
“best.”
The three arguments to the function search are a node start in a labeled tree,
a label goal to search for, and the data algebra parameter struct. We assume that
one tree node is labeled with the goal, so there is no error test. The result of a
call to search is the first node reached, starting from start, whose label matches
goal. The tree structure is declared at the top of the program fragment to make
the types of the tree functions explicit. The tree has a root, each node has a label
and is either a leaf or has two descendants. The function is-leaf? tests whether
a node is a leaf, while left and right return the left and right descendants of any
nonleaf.
3.6 Reduction Rules and intuitive Semantics of Existential Types
Intuitively, the meaning of the abstype expression
abstype t with x: CTis (pack TM to 3 t.a) in N
is the meaning of N in an environment where t is bound to 7, and x to M.
Operationally, we can evaluate abstype expressions using the reduction rule
abstype t with x: (T is (pack TM to 3 t.a) in N + [M/x][~/t]lV,
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type 485
I’ Search returns first node reached from sfurr with label(node) = goal.
The structure parameter may be a stack, queue, priority queue, etc. */
let search(start:t, goahstring, strnct: VEls[s/\(tAs-s)r\(s-Us)]) =
abstype s with empty:s, insert:t/\s-s, delete:s-tAs
is stnlct {t}
in
1’ function to select next node; also returns updated structure ‘/
let next(node:t, st:s) =
if isleaf?(node) then delete(st)
else delete(insert(left(node), insert(right(node),st)))
in
/* recursive function jind calls near until goal reached +/
letrec find(node:t, st:s) =
if label(node)=goal then node else find(next(node, St))
in
/* callfind to reach node with label(node)=goaL*/
find(start, empty)
end
end
end
in
.. /* program using search function *I
end
end
where substitution includes renaming of bound variables as usual. (It is not too
hard to prove that the typing rules of SOL guarantee that [iV/x][~/t]N is well-
typed.) Since abstype binds variables, we also have the renaming equivalence
It is interesting to compare abstype with case since V-types with inleft, inright,
and case correspond to finite categorical sums. Essentially, abstype is an
infinitary version of case.
As an aside, we note that the binding construct abstype may be replaced by a
constant sum. This treatment of abstype points out that the binding aspects of
abstype are essentially X binding. If N is a term with type u + p, and t is not
free in p, then both Xt.N and C t.N are well typed. Therefore, it suffices to have
a function sum 3t.a p that maps Xt.N:Vt.[a + p] to C t.N: (3 t.a) + p.
Essentially, this means sum 3 t.u p must satisfy the equation
(sum 3t.a px)(pack my to 3t.a) = x(~)y
for any x, y of the appropriate types. In the version of SOL with sum as basic,
we use this equation, read from left to right, as the defining reduction rule for
sum. Given sum, both C and abstype may be defined by
C t.M ::= sum 3 t.u p Xt.M,
abstype t with x: u is N in M ::= (C th: a.M)N.
The reduction rules for C and abstype follow the reduction rules for sum. From
a theoretical point of view, it would probably be simpler to define SOL using
sum instead of C or abstype, since this reduces the number of binding operators
in the language. However, for expository purposes, it makes sense to take abstype
as primitive, since this makes the connection with data abstraction more readily
apparent. The difference is really inessential since any one of C, abstype, and
sum may be used to define the other two (using other constructs of the language).
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type l 487
Let qE be the congruent and transitive closure of =s~. Then we have the
following theorem:
STATIC TYPING THEOREM. Let M, N be two terms of SOL with Type*(M) =
TypeA( Then M +c= N iff Erase(M) aE Erase(N).
Since the theorem shows that two sets of reduction rules have essentially
equivalent results, it follows that programs may be executed using any interpreter
or compiler on the basis of untyped reduction rules. Like the Type Preservation
Theorem, the proof uses induction on the length of reduction paths and is
essentially straightforward. Although easily proved, these theorems are important
since they confirm our expectations about the relationship between typing and
program execution.
It is worth mentioning the relationship between the Static Typing Theorem
and the seemingly contradictory “folk theorem” that tagged sums (in SOL
notation, g V T types) require run-time type information. Both are correct but
based on different notions of “untyped” evaluation. The Static Typing Theorem
says that if a term M is well typed, then M can be evaluated using untyped
reduction => E. However, notice that Erase does not remove inleft and inright,
only the type designations on these constructs. Therefore, in evaluating a case
statement
case M left ... right . . . end
the untyped evaluation rules can depend on whether M is of the form inleft M,
or inright Ml. In the “ folk theorem,” this is considered type information, hence
the apparent contradiction.
The SOL reduction rules have several other significant properties. For example,
the reduction rules have the Church-Rosser property [22,61].
CHURCH-R• SSERTHEOREM. Suppose M is a term of SOL which reduces to
Ml and M2. Then there is a term N such that both M, and Mz reduce to N.
In contrast to the untyped lambda calculus, no term of SOL can be reduced
infinitely many times.
STRONG NORMALIZATION THEOREM. There are no infinite reduction se-
quences.
The strong normalization theorem was first proved by Girard [22]. In light of
the strong normalization theorem, the Church-Rosser theorem follows from a
simple check of the weak Church-Rosser property (see Proposition 3.1.25 of [2]).
A consequence of Church-Rosser and Strong Normalization is that all maximal
reduction sequences (from a given term) end in the same normal form.3 As
proved in Girard’s thesis [22] and discussed in [20] and [59], the proof of the
strong normalization theorem cannot be carried out formally in either Peano
arithmetic or second-order Peano arithmetic (second-order Peano is also called
“analysis”). Furthermore, the class of number-theoretic functions that are
LIA normal form M is a term that cannot be reduced. Our use of the phrase strong normalization
follows [2]. Some authors use strong normalization for the property that all maximal reduction
sequences from a given term end in the same normal form.
ACM Transactions on Programming Languages and Systems, Vol. 10, NO. 3, July 1988
Abstract Types Have Existential Type 489
representable in pure SOL without base types are precisely the recursive functions
that may be proved total in second-order Peano arithmetic [22, 681. These and
related results are discussed in [20] at greater length.
Since Pebble does not supply projection functions for dependent products, the
dependent product of Pebble actually seems to be a sum (in the sense of category
theory), like SOL g-types. KR dependent products do have something that looks
like a projection function: If A is a data algebra, then Currier(A) is a type
expression of KR. However, since Carrier(pack TM to 3 La) is not considered
equal to 7, it seems that KR dependent products are not truly products. Perhaps
further analysis will show that KR dependent products are also sums and closer
to SOL existential types than might appear at first glance.
As pointed out in [30], there are actually two reasonable notions of sum type,
“weak” and “strong” sums. The SOL existential type is a typical example of weak
sums, whereas strong sums appear as the C-types of Martin Lof’s type theory
[46]. The main difference lies in rule (AB.3), which holds for weak sums, but not
for strong. Thus, while Martin-Lof’s product types over universes give a form of
polymorphism that is similar to SOL polymorphism, Martin-Lof’s sum types
differ from our existential types. For this reason, the languages are actually quite
different. In addition, the restrictions imposed by universes simplify the seman-
tics of Martin-Lof’s language, at the cost of a slightly more complicated
syntax. (Some relatively natural programming examples, such as the Sieve of
Eratosthenes program given in Section 5.2 of this paper, are prohibited by
the universe restrictions of Martin-Lof type theory.) For further discussion of
sum and product types over universes, the reader is referred to [9], [lo], [31],
[451, [461, [491, and [541.
4. FORMULAS AS TYPES
4.1 Introduction
The language SOL exhibits an analogy between logical formulas and types that
has been used extensively in proof theory [12, 13, 22, 30, 35, 38, 39, 46, 67, 691.
The programming significance of the analogy has been stressed by Martin-Lof
[46]. We review the basic idea using propositional logic and then discuss quan-
tification briefly. In addition to giving some intuition into the connection between
computer science and constructive logic, the formulas-as-types analogy also
suggests other languages with existential types. One such language, involving
specifications as types, is discussed briefly at the end of this section. In general,
our analysis of abstype suggests that any constructive proof rules for existential
formulas provide data type declarations. For this reason, the formulas-as-types
languages provide a general framework for studying many aspects of data
abstraction.
as simply being true or false whenever we assign truth values to each variable.
While various forms of intuitionistic semantics have been developed [IO, 33, 34,
701, we will not go into this topic. Instead, we will characterize intuitionistic
validity by means of a proof system.
Natural deduction is a style of proof system that is intended to mimic the
common blackboard-style argument
Assume u.
By . . . we conclude 7.
Therefore u + 7.
We make an assumption in the first line of this argument. In the second line,
this assumption is combined with other reasoning to derive 7. At this point, we
have proved T, but the proof depends on the assumption of u. In the third step,
we observe that since u leads to a proof of 7, the implication 6 + r follows. Since
the proof of u + r is sound without proviso, we have “discharged” the assumption
of u in proceeding from 7 to u + T. In a natural deduction proof, each proposition
may depend on one or more assumptions. A proposition is considered proved
only when all assumptions have been discharged.
The natural deduction proof system for implicational propositional logic
consists of three rules, given below. For technical reasons, we use labeled
assumptions. (This is useful from a proof-theoretic point of view as a means of
distinguishing between different assumptions of the same formula.) Let V be a
set, intended to be the set of labels, and let A be a mapping from labels to
formulas. We will use the notation Conseq,(M) = u to mean that M is a proof
with consequence u, given the association A of labels to assumptions. Proofs and
their consequences are defined as follows:
ConseqA(M) does not depend on A.) Even when ---) is the only propositional
connective, there are classical ,tautologies that are not intuitionistically provable.
For example, it is easy to check that the formula ((s + t) + s) + s is a classical
tautology just by trying all possible assignments of true and false to s and t.
However, this formula is not intuitionistically provable.
Of course, we have just defined the typed lambda calculus: The terms of typed
lambda calculus are precisely the proofs defined above and their types are the
formulas given. In fact, ConseqA and TypeA are precisely the same function, and
Assume(M) is precisely the set of free variables of M. The similarity between
natural deduction proofs and terms extends to the other connectives and quan-
tifiers. The proof rules for A, V, V, and 3 are precisely the formation rules given
earlier for terms of these types.
One interesting feature of the proof rule for V of [60] is that it is the
discriminating case statement of CLU [42], rather than the problematic outleft
and outright functions of ML [23]. The “out” functions of ML are undesirable
since they rely on run-time exceptions (cf. [41], p. 569). Specifically, if X: r
in ML, then (inright 3~): cf V 7 and outleft(inright x): 6. However, we cannot
actually compute a value of type g from x : T, so this is not semantically sensible.
The ML solution to this problem is to raise a run-time exception when
outleft(inright X) is evaluated, which introduces a form of run-time type
checking. Since the V rule leads us directly to a case statement that requires no
run-time type checking, it seems that the formulas-as-types analogy may be a
useful guide in designing programming languages.
and
M2: (3t.a) */I.
We will say that MI is universally parameterized and MZ is ex&entially pararm+
terized.
Generic packages are universally parameterized data algebras. For example,
given any type t with operations
plus: t A t + t
times: t A t + t,
we can write a data algebra t-matrix implementing matrix operations over t. Four
operations we might choose to include are
create: t A ... A t-mat
mplus: mat A mat + mat,
mtimes: mat A mat * mat,
a!&: mat + t.
If mbody is an expression of the form
mbody ::= pack 7M1 . . - M, to 3s[(t A -. - A t + s)
A (s A s + s) A (s A s + s) A (s + t)]
implementing create, m&s, mtimes, and det using plus and times, then
matrix ::= At. Aplus: t A t + t. Xtimes: t A t + t.mbody
is a universally parameterized data algebra. The type of matrix is
Vt.(t A t + t) --, (t A t + t) + 3s[(t A . . . A
t + s) A (s A s + s) A (s A s + s) A (s + t)].
Note that mbody could not be existentially parameterized by t since t appears
free in the type of mbody.
Functions from data algebras to data algebras are existentially parameterized.
One simple manipulation of data algebras is to remove operations from the
signature. For example, a doubly ended queue, or dequeue, has two insert and
two remove operations. The type of an implementation dq of dequeues with
empty, insertl, insert2, removel, and remove2, is
dq-type ::= Vt.3d.[d A (t A d + d) A
(t A d + d) A (d + t A d) A (d + t A d)]
A function that converts dequeue implementations to queue implementations
is a simple example of an existentially parameterized structure. Given dq, we can
implement queues using the form
Q(x, t) ::= abstype d with empty: . . . , insertl: . . . , insert2: . . . ,
removel: . . . , remove2: . . .
is x(t]
in pack d empty insert1 remove2 to 3 t.a
with type
&-type ---, Vt. 3s.[s A (t A s + s) A (s + t A s) ]
is a function from data algebras to data algebras. Suppose that queue is the data
algebra produced by applying dq-to-q to dq. Since the type of queue is a closed
type expression, the fact that queue uses the same representation type as dq
seems effectively hidden. Generally, universal parameterization may be used to
effect some kind of sharing of types, whereas existential parameterization ob-
scures the identity of representations. (See [45], which was written later, for
related discussion.)
Some other useful transformations on data algebras are the analogs of the
theory building operations combine, enrich, and derive of CLEAR [5,6]. Although
a general combine operation as in CLEAR, for example, cannot be written in
SOL because of type constraints, we can write a combine operation for any pair
of existential types. For example, we can write a procedure to combine data
algebras of types 3s.~ and 3 t.p into a single data algebra. The type of this
function
Combine, = Xx: 3 t.u Xy : 3 t.p.
abstype s with z: u is x in
abstype t with w : p is y in
packs [pack t(z, w) to 3t(u A p)] to 3s3t(u A p)
is
Combine,: 3s.~ + 3t.p 4 3s3t(u A p).
For universally parameterized data algebras of types V r 3 S.CTand V r 3 t.p, we can
write combine so that in the combined data algebra, the type parameter will be
shared. The combine function with sharing
Combines = Xx:VrZls.a XyzVr3t.p.
Xr.abstype s with z : tr is x(r) in
abstype t with w:p is y(r) in
packs [pack t(z, w) to 3t(u A p)] to 3s3t(u A p)
has type
Combinez: Vr3s.a + Vr3t.p + Vr3s3t(u A p).
A similar, but slightly more complicated, combine function can be written for
the case in which the two parameters are both universally parameterized by a
type and several operations on the type. For example, a polymorphic matrix
package could be combined with a polymorphic polynomial package to give a
combined package parameterized by a type t and two binary operations plus and
times providing both matrices and polynomial over t. Furthermore, the combine
function could be written to enrich the combined package by adding a function
that finds the characteristic polynomial of a matrix.
5.2 Data Structures Using Existential Types
Throughout this paper, we have viewed data algebras as implementations of
abstract data types. An alternative view is that data algebras are simply records
tagged with types. This view leads us to consider using data algebras as parts of
data structures. In many cases, these data structures do not seem directly related
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
496 - J. C. Mitchell and G. D. Plotkin
to any kind of abstract data type. The following example uses existentially typed
data structures to represent streams.
Intuitively, streams are infinite lists. In an applicative language, it is convenient
to think of a stream as a kind of “process” that has a set of possible internal
states and a specific value associated with each state. Since the process imple-
ments a list, there is a designated initial state and a deterministic state transition
function, Therefore, a stream consists of a type s (of states) with a designated
individual (start state) of type s, a next-state function of type s + s, and a value
function of type s --, t, for some t. An integer stream, for example, will
have a value function of type s + int, and so the type of integer streams will be
3s[s A (s ---) s) A (s --, int)].
The Sieve of Eratosthenes can be used to produce an integer stream enumer-
ating all prime numbers. This stream is constructed using a sift operation on
streams. Given an integer stream sl, Sift(sl) is a stream of integers that are not
divisible by the first value of sl. If Num is the stream 2, 3, . . . , then the sequence
formed by taking the first value of each stream
Num, Sift(Num), Sift(Sift(Num)), ...
will be the sequence of all primes.
With streams represented using existential types, Sift may be written as the
following function over existential types.
Sift =
X stream: 3s[s A (s --, s) A (s - int)].
abstype s with start : s, next : s -+ s, value : s + int is stream
in let n = value(start)
in letrec f = X state : s.
if n divides value(state) then f (next(state))
else state
in
pack s f (start) Xx: s.f (next(x)) value to 3s[s A (s + s) A (s + int)]
end
end
end
Sieve will be the stream with states represented by integer streams, start state
the stream of all integers greater than 1, and Sift the successor function on states.
The value associated with each Sieve state is the first value of the integer stream,
so that the values of Sieve enumerate all primes.
Sieve =
abstype s with start : s, next: s + s, value : s + int
ispack 3t[tA (t-t) A (t-+int)]
packint2 Successor Xx:int.r to 3t[t A (t-t) A (t+int)]
Sift
Xstate:Yt[tA(t+t)A(t-+int)].
abstype F with r-start, r-next, r-val is state
in r-val( r-start)
to!lt[tA(t+t)A(t+int)]
Expressed in terms of Sieve, the ith prime number is
abstype s with start : s, next : s + s, value : s + int
is Sieve
in value(next’ start),
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type 497
where “next’ start” is the expression next(next(. . . (next start). . .)) with i occur-
rences of next.
It is worth noticing that Sieve is “circular” in the sense that the representation
type 3t[t A (t + t) A (t + int)] used to define Sieve is also the type of Sieve
itself. For this reason, this example could not have been written in a predicative
system like Martin-Lof’s intuitionistic type theory [9, 461. The typing rules of
that theory require that elements of one type be composed only of elements of
simpler types.
and type binding can be added to M to produce a well-typed term of SOL. Some
questions of this nature are discussed in [40], [48], and [53].
A general problem in the study of types is a formal characterization of type
security. We have given two theorems about typing in SOL: Expressions may be
evaluated without considering type information, and the syntactic type of an
expression is not affected by reducing the expression to simpler forms. These
theorems imply that types may be ignored when evaluating SOL expressions and
that SOL type checking is sufficient to prevent run-time type errors. The study
of representation independence (mentioned above) leads to another notion of
type security, but further research seems necessary to show that SOL programs
are “type-safe” in other ways.
One interesting aspect of SOL is that it may be derived from quantified
propositional (second-order) logic using the formulas-as-types analogy discussed
in Section 4. Our analysis of abstype demonstrates that the proof rules for
existential formulas in a variety of logical systems all correspond to declaring
and using abstract data types. Thus, the formulas-as-types languages provide a
general framework for studying abstract data types. In particular, the language
derived from first- and second-order logic seems to incorporate specifications
into programs in a very natural way. The semantics and programming properties
of this language seem worth investigating and relating to other studies of data
abstraction based on specification.
where t is any type variable and c is any type constant. (We use two sorts of
variables, type variables r, s, t, . . . and ordinary variables X, y, z, . . . )
A type assignment A is a function from ordinary variables to type expressions.
We use A[x : u] to denote the type assignment A, with A1 (y) = A (y) for y different
from X, and A, (x) = u. The partial functions TypeA, for all type assignments A,
and the operational semantics of SOL are defined as follows:
TypeA = u + 7, TypeA = u
TypeA = 7
Xx : u.M = Xy : u.[ y/x]M, y not free in M
(Xx : u.M)N =+ [N/x]M,
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
Abstract Types Have Existential Type 499
Products
Type,(M) = (r, TypeA = 7
Type,.,((M, N)) = (r A T
TypeA = (r A 7
TypeA ( fst M) = u, TypeA (snd M) = T
fst(M, N) =9 M, snd(M, N) + N
Sums
Typea = u
TypeA (inleft M to CTV 7) = u V 7, TypeA (inright A4 to T V a) = 7 V u
TseA(M) = u V 7, Twq,:,1W) = P, Twqr:rl(P) = P
Type, (case M left x : UN right y : r.P end) = p
case M left x: u.N right y: 7.P end
= case M left u: u.[u/x]N right u: ~.[u/y]P end,
ACKNOWLEDGMENTS
Thanks to John Guttag and Albert Meyer for helpful discussions. Mitchell thanks
IBM for a graduate fellowship while at MIT, and Plotkin acknowledges the
support of the BP Venture Research Unit.
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.
500 l J. C. Mitchell and G. D. Plotkin
REFERENCES
1. ARBIB, M. A., AND MANES, E. G. Arrows, Structures, and Functors: The Categorical Imperative.
Academic Press, Orlando, Fla., 1955.
2. BARENDREGT, H. P. The Lambda Calculus: Its Syntax and Semantics. North-Holland, Amster-
dam, The Netherlands, 1984 (revised edition).
3. BRUCE, K. B., AND MEYER, A. A completeness theorem for second-order polymorphic lambda
calculus. In Proceedings of the International Symposium on Semantics of Data Types. Lecture
Notes in Computer Science 173, Springer-Verlag, New York, 1984, pp. 131-144.
4. BRUCE, K. B., MEYER, A. R., AND MITCHELL, J. C. The semantics of second-order lambda
calculus. In Information and Computation (to be published).
5. BURSTALL, R. M., AND GOGUEN, J. Putting theories together to make specifications. In Fifth
International Joint Conference on Artificial Intelligence, 1977, pp. 1045-1958.
6. BURSTALL, R. M., AND GOGUEN, J. An informal introduction to specification using CLEAR.
In The Correctness Problem in Computer Science, Boyer and Moore, Eds. Academic Press,
Orlando, Fla., 1981, pp. 185-213.
7. BURSTALL, R., AND LAMPSON, B. A kernel language for abstract data types and modules. In
Proceedings of International Symposium on Semantics of Data Types. Lecture Notes in Computer
Science 173, Springer-Verlag, New York, 1984, pp. l-50.
8. CONSTABLE, R. L. Programs and types. In 21st IEEE Symposium on Foundations of Computer
Science (Syracuse, N.Y., Oct. 1980). IEEE, New York, 1980, pp. 118-128.
9. CONSTABLE, R. L., ET AL. Implementing Mathematics With The Nuprl Proof Deuelop-
ment System. Graduate Texts in Mathematics, vol. 37, Prentice-Hall, Englewood Cliffs, N.J.,
1986.
10. COQUAND, T. An analysis of Girard’s paradox. In Proceedings of the IEEE Symposium on Logic
in Computer Science (June 1986). IEEE, New York, 1986, pp. 227-236.
11. COQUAND, T., AND HUET, G. The calculus of constructions. Znf. Comput. 76, 2/3 (Feb./Mar.
1988), 95-120.
12. CURRY, H. B., AND FEYS, R. Combinatoty Logic I. North-Holland, Amsterdam, 1958.
13. DEBRUIJN, N. G. A survey of the project Automath. In To H. Z3. Curry: Essays on Com-
binatory Logic, Lambda Calculus and Formalism. Academic Press, Orlando, Fla., 1980, pp.
579-607.
14. DEMERS, A. J., AND DONAHUE, J. E. Data types, parameters and type checking. In 7th ACM
Symposium on Principles of Programming Languages (Las Vegas, Nev., Jan. 28-30, 1980). ACM,
New York, 1980, pp. 12-23.
15. DEMERS, A. J., AND DONAHUE, J. E. ‘Type-completeness’ as a language principle. In 7th ACM
Symposium on Principles of Programming Languages (Las Vegas, Nev., Jan. 28-30, 1980). ACM,
New York, 1980, pp. 234-244.
16. DEMERS, A. J., DONAHUE, J. E., AND SKINNER, G. Data types as values: polymorphism, type-
checking, encapsulation. In 5th ACM Symposium on Principles of Programming Languages
(Tucson, Ariz., Jan. 23-25,1978). ACM, New York, 1978, pp. 23-30.
17. U.S. DEPARTMENT OF DEFENSE Reference Manual for the Ada Programming Language. GPO
008.ooo-00354-8,198O.
18. DONAHUE, J. On the semantics of data type. SIAM J. Comput. 8 (1979), 546-560.
19. FITTING, M. C. Zntuitionistic Logic, Model Theory and Forcing. North-Holland, Amsterdam,
1969.
20. FORTUNE, S., LEIVANT, D., AND O’DONNELL, M. The expressiveness of simple and second
order type structures. J. ACM 30,l (1983), 151-185.
21. GIRARD, J.-Y. Une extension de l’interpretation de Godel i l’analyse, et son application i
l’elimination des coupures dans l’analyse et la theorie des types. In 2nd Scandinavian Logic
Symposium, J. E. Fenstad, Ed. North-Holland, Amsterdam, 1971, pp. 63-92.
22. GIFWRD, J.-Y. Interpretation fonctionelle et elimination des coupures de l’arithmetique d’ordre
superieur. These D’Etat, Univ. Paris VII, Paris, 1972.
23. GORDON, M. J., MILNER, R., AND WADSWORTH, C. P. Edinburgh Lecture Notes in Computer
Science 78, Springer-Verlag, New York, 1979.
24. GRATZER G. Universal Algebra. Van Nostrand, New York, 1968.
25. GUT-TAG, J. V., HOROWITZ, E., AND MUSSER, D. R. Abstract data types and software validation.
Commun. ACM 21,12 (Dec. 1978). 10481064.
ACM Transactions on Programming Languages and Systems, Vol. lo, No. 3, July 1988.
Abstract Types Have Existential Type 501
50. MILNER, R. The standard ML core language. Polymorphism 2, 2 (1985), 28 pages. An earlier
version appeared in Proceedings of 1984 ACM Symposium on Lisp and Functional Programming.
51. MITCHELL, J. C. Semantic models for second-order Lambda calculus. In Proceedings of the
25th IEEE Symposium on Foundations of Computer Science (1984). IEEE, New York, 1984,
pp. 289-299.
52. MITCHELL, J. C. Representation independence and data abstraction. In Proceedings of the
13th ACM Symposium on Principles of Programming Languages (St. Petersburg Beach, Fla.,
Jan. 13-15, 1986). ACM, New York, 1986, pp. 263-276.
53. MITCHELL, J. C. Polymo?phic type inference and containment. Inf. Comput. 76,2/3 (Feb./Mar.
1988), 211-249.
54. MITCHELL, J. C., AND HARPER, R. The essence of ML. In Proceedings of the 15th ACM
Symposium on Principles of Programming Languages (San Diego, Calif., Jan. 13-15,1988). ACM,
New York, 1988, pp. 28-46.
55. MITCHELL, J. C., AND MEYER, A. R. Second-order logical relations. In Log& of Programs.
Lecture Notes in Computer Science 193, Springer-Verlag, New York, 1985, pp. 225-236.
56. MITCHELL, J. C., AND PLOTKIN, G. D. Abstract types have existential types. In Proceedings
of the 12th ACM Symposium on Principles of Programming Languages (New Orleans, La.,
Jan. 14-16, 1985). ACM, New York, 1985, pp. 37-51.
57. MITCHELL, J. G., MAYBERRY, W., AND SWEET, R. Mesa language manual. Tech. Rep. CSL-
79-3, Xerox PARC, Palo Alto, Calif., 1979.
58. MORRIS, J. H. Types are not sets. In 1st ACM Symposium on Principles of Programming
Languuges (Boston, Mass., Oct. l-3, 1973). ACM, New York, 1973, pp. 120-124.
59. O’DONNELL, M. A practical programming theorem which is independent of Peano arithmetic.
In 11th ACM Symposium on the Theory of Computation (Atlanta, Ga., Apr. 30-May 2, 1979).
ACM, New York, 1979, pp. 176-188.
60. PRAWITZ, D. Natural Deduction. Almquist and Wiksell, Stockholm, 1965.
61. PRAWITZ, D. Ideas and results in proof theory. In 2nd Scandinavian Logic Symposium. North-
Holland, Amsterdam, 1971, pp. 235-308.
62. REYNOLDS, J. C. Towards a theory of type structure. In Paris Colloquium on Programming.
Lecture Notes in Computer Science 19, Springer-Verlag, New York, 1974, pp. 408-425.
63. REYNOLDS, J. C. The essence of Algol. In Algorithmic Languages, J. W. de Bakker and J. C.
van Vliet, Eds. IFIP, North-Holland, Amsterdam, 1981, pp. 345-372.
64. REYNOLDS, J. C. Types, abstraction, and parametric polymorphism. In IFIP Congress (Paris,
Sept. 1983).
65. REYNOLDS, J. C. Polymorphism is not set-theoretic. In Proceedings of International Symposium
on Semantics of Data Types. Lecture Notes in Computer Science 173, Springer-Verlag, New York,
1984, pp. 145-156.
66. SHAW, M. (Ed.) ALPHARD: Form and Content. Springer-Verlag, New York, 1981.
67. STATMAN, R. Intuitionistic propositional logic is polynomial-space complete. Theor. Comput.
Sci. 9 (1979), 67-72.
68. STATMAN, R. Number theoretic functions computable by polymorphic programs. In 22nd IEEE
Symposium on Foundations of Computer Science. IEEE, New York, 1981, pp. 279-282.
69. STENLUND, S. Combinators, X-terms and Proof Theory. Reidel, Dordrecht, Holland, 1972.
70. TROELSTRA, A. S. Mathematical Investigation of Zntuitionistic Arithmetic and Analysis. Lecture
Notes in Mathematics 344, Springer-Verlag, New York, 1973.
71. WULF, W. W., LONDON, R., AND SHAW, M. An introduction to the construction and verification
of Alphard programs. IEEE Trans. Softw. Eng. SE-2 (1976), 253-264.
ACM Transactions on Programming Languages and Systems, Vol. 10, No. 3, July 1988.