Virtual types have been proposed as a notation for generic programming in object-oriented languages|an alternative to the more familiar mechanism of parametric classes. The tradeo s between the two mechanisms are a matter of current debate: for many examples, both appear to o er convenient (indeed almost interchangeable) solutions; in other situations, one or the other seems to be more satisfactory. However, it has proved di cult to draw rigorous comparisons between the two approaches, partly because current proposals for virtual types vary considerably in their details, and partly because the proposals themselves are described rather informally, usually in the complicating context of full-scale language designs. Work on the foundations of object-oriented languages has already established a clear connection between parametric classes and the polymorphic functions found in familiar typed lambda-calculi. Our aim here is to explore a similar connection between virtual types and dependent records. We present, by means of examples, a straightforward model of objects with embedded type elds in a typed lambda-calculus with subtyping, type operators, xed points, dependent functions, and dependent records with both bounded" and manifest" type elds (this combination of features can be viewed as a measure of the inherent complexity of virtual types). Using this model, we then discuss some of the major di erences between previous proposals and show why some can be checked statically while others require run-time checks. We also investigate how the partial \duality" of virtual types and parametric classes can be understood in terms of translations between universal and (dependent) existential types.

Language support for generic programming plays an important role in the
development of reusable libraries. In object-oriented languages, two di erent
approaches to genericity have been considered. The more familiar one|based
closely on the classical parametric polymorphism of functional languages such
as ML and Haskell|can be found, for example, in the template mechanism of
C++ [

The static typing of virtual types is not yet clearly understood. Indeed, early
proposals were statically unsafe, requiring extra runtime checks; more recent
work has produced several proposals for type-safe variants [

Our goal in this paper is to establish a rigorous setting in which to understand
and discuss the basic mechanisms of virtual types. Following a long line of past
work on foundations for object-oriented programming (see [

The rest of the paper is organized as follows. Section 2 reviews the idea
of virtual types by means of a standard example, the animal/cow class
hierarchy of Shang [

Our presentation is self-contained, but somewhat technical at times.
Familiarity with past work on modeling objects in typed lambda-calculi (e.g., [

We begin by reviewing the notion of virtual types through an example. This
example, used throughout the paper, is a variant of the animal/cow example of
Shang [

We begin by de ning a generic class of animals, along with its interface. 1 Referring to this approach with the phrase \virtual types" is somewhat confusing, since|as we will see|these type members may or may not be \virtual" in the sense of virtual or abstract methods. But the terminology is standard.

interface AnimalI { type FoodType <: Food; void eat (FoodType f); void eatALot (FoodType f); } virtual class Animal

implements AnimalI { virtual type FoodType <: Food; virtual void eat (FoodType f); void eatALot (FoodType f) { eat(f); eat(f); }} Every animal has methods eat and eatALot, both accepting some food as an argument. The body of the eat method, which is speci c to particular kinds of animals, is omitted; the virtual marker defers the responsibility of providing an implementation to subclasses. (We use the C++ keyword virtual in preference to Java's abstract to avoid terminological confusion: locutions like \abstract type" already have a well-established meaning.) The calls to eat from the body of the eatALot method will call whatever body is provided by the subclass.

Similarly, the class Animal defers specifying exactly what kind of food a given kind of animal likes to eat. The virtual member FoodType acts as placeholder for this type, allowing it to be mentioned in the types of eat and eatALot, just as the declaration of eat provides a placeholder for its eventual implementation, allowing it to be referred to from the body of eatALot. Classes with virtual members (either types or methods) cannot be instantiated, since they are incomplete: they can only be subclassed.

The interface AnimalI speci es that every animal object has three members: a type FoodType and methods eat and eatALot. The FoodType member of every animal is known to be some kind of Food (FoodType<:Food), but, since di erent animals eat di erent kinds of food, the exact identity of this type is not visible. It follows immediately that it is not possible to feed an animal without knowing what kind of animal it is: if a is an object of type AnimalI, then a's eat method requires an argument of type a.FoodType; but there is no way to obtain a value of this type (except, perhaps, by building a nutrient-free empty value using new).

Speci c kinds of animals are modeled by classes inheriting from Animal. For example, here is a Cow class and its interface: interface CowI

extends AnimalI { type FoodType $ Grass; } class Cow extends Animal

implements CowI { final type FoodType $ Grass; void eat (FoodType f) { ... }} In Cow, the virtual method eat is given a concrete implementation (shown as \..."). Similarly, the virtual type member FoodType is given a concrete value, Grass. The annotation final on the FoodType member means that it cannot be rede ned by subclasses: every subclass of Cow is guaranteed to have Grass as its FoodType. The interface CowI reflects the fact that FoodType is nal: in e ect, it tells the world that every cow eats food whose type is equal to Grass. Thus, given an object a of type CowI, we may validly obtain some grass from any source and pass it to the eat or eatALot methods.

Virtual types are also useful in more standard examples of generic
programming. For example, a generic Bag class can be de ned with a virtual type
ElementType. Then classes NatBag, StringBag, etc. can be de ned by
inheriting from Bag and giving ElementType a final binding to Nat or String. Other
examples of generic programming with virtual types can be found in [

It is well understood [29, 6, etc.] how parametric classes|classes abstracted
on type parameters|can be understood as polymorphic functions in a typed
lambda-calculus. By analogy, objects with type members should clearly be
modeled as some kind of records with type elds. Fortunately, such records have been
studied extensively in the type-theory literature (e.g. [

The typed lambda-calculus sketched in this section is based directly on these
intuitions. In essence, it can be described as System F! (the omega-order
polymorphic lambda-calculus with subtyping [

The core of the system is Girard's System F! [

plusfour = double Nat plustwo; is a fancy way of writing the function that adds four to its (numeric) argument.

Parametric types are written in a similar style. For example,

Pair = [A:*] [B:*] {|fst:A, snd:B|};

PairNatNat = Pair Nat Nat; is a convenient abbreviation for the parametric type of pairs, and is the concrete type of pairs of numbers. The usual (polymorphic) operations on pairs can be de ned as follows: fst = [A:*] [B:*] [p: Pair A B] p.fst; snd = [A:*] [B:*] [p: Pair A B] p.snd; pair = [A:*] [B:*] [a:A] [b:B] ({fst=a, snd=b} :: Pair A B); The types of these operations are: fst : 8[A:*] 8[B:*] Pair A B ! A snd : 8[A:*] 8[B:*] Pair A B ! B pair : 8[A:*] 8[B:*] A ! B ! Pair A B (In the following, we will often display de ned terms together with their types.) Note that the de nition of pair uses an explicit coercion (:: Pair A B) to control how its type is printed by the typechecker. Leaving it o results in a de nition with exactly the same behavior pair = [A:*] [B:*] [a:A] [b:B] {fst=a, snd=b}; pair : 8[A:*] 8[B:*] A ! B ! {|fst:A, snd:B|} (since we have de ned Pair A B to be interchangeable with {|fst:A,snd:B|}), but less intuitive for the reader.

To ensure their well-formedness, types and type operators are assigned kinds,

are ordinary types; type expressions of kind *!* are functions from types to types; etc.

It is sometimes useful to write higher-order type operators|that is, type operators whose arguments are type operators. For example,

BothBool = [F:*!*!*] F Bool Bool; is higher-order type operator that, when applied to any operator O, yields the type O Bool Bool. Thus: mypair = pair Bool Bool true false :: BothBool Pair; A more natural example of higher-order type operators will be seen later in the Object type constructor: its argument I is itself an operator abstracted over the \self type" Rep.

For constructing objects, we shall also need a xed-point constructor. If t is a function from T to T, then fix T t is its xed point. (Writing T explicitly simpli es the typechecking of fix in the presence of dependent types.) For example, here is how fix is used to construct a factorial function: fact = fix (Nat!Nat) [f:Nat!Nat] [n:Nat]

if eq n 0 then 1 else times n (f (pred n)) :: Nat ! Nat; 3.2

Next, we add the familiar notion of subtyping. For example, subtyping of function types is contravariant on the left and covariant on the right. The subtype relation has a maximal element, called Top. Constraining a type variable to be a subtype of Top is actually no constraint at all, so we can recover unbounded quanti cation from bounded, writing [X<:Top]t in place of [X:*]t. (We will continue to write [X:*]t in what follows, for readability.)

Subtyping is extended pointwise to type operators: [X:K]S is a subtype of [X:K]T if S is a subtype of T under all legal substitutions for X.

?; X:K ` S <: T ? ` [X:K]S <: [X:K]T

subtype of Top. 3.3

To support records with type elds, a bit of machinery is required. First, we must deal with the fact that later elds in a record may refer to earlier elds by name|e.g., the type of the eat eld must refer to the FoodType eld. (Thus, in particular, the order of elds is signi cant in dependent records.) Second, we must be able to deal with record-projection expressions like a.FoodType appearing in the types of values (e.g., a.eat). The second requirement in particular goes somewhat beyond what can be expressed using ordinary existential types, taking us into the realm of dependent records.

eld of one of two forms: either a term eld xi=ti or a type eld Xi=Ti. The name xi or Xi is not only used to project a record from outside but also is a binder whose scope is the rest of the elds in the record.3 For example, in the record value r = {X=Nat,x= [y:X]y+1}, X in the second eld is bound by the rst occurrence of X.

A record type has the form {|Bii21 n|}, where Bi is a binding of one of three forms: a term binding x:T, a bounded type binding X<:T, or a manifest type binding X$T. (In examples, we will also use type bindings of the form X:* as an abbreviation for X<:Top.) For example, the record r above has type {|X$Nat,x:X!X|}. A less informative type also possessed by r is {|X<:Top,x:X!X|}, which hides the representation of X and corresponds to the usual existential type 9X.X!X. In order to remind us of a connection to existential types, we sometimes write 9 before a eld name in records or record types, like {|9X<:Top,x:X!X|}, although 9 itself doesn't have a signi cant meaning. Formally, the typing rule for record introduction is: ?; B1; : : : ; Bj?1 ` j : Bj j21 n

? ` {|Bii21 n|} : * ? ` { ii21 n} : {|Bii21 n|} Each eld de nition i must satisfy the corresponding binding Bi under a context augmented with the information of the preceding elds (?; B1; : : : ; Bi?1). Term elds xi=ti satisfy bindings of the form xi:Ti; type elds Xi=Ti satisfy manifest type bindings Xi$Ti. (Note that we cannot directly derive a record type with a bounded type binding using the rule above . For example, the type given to r above is {|9X$Nat,x:X!X|}. If we want to hide the identity of X and give r the abstract type {|9X:*,x:X!X|}, we must use the usual subsumption rule plus the record subtyping rules discussed below.)

The rule for record projections is basically the same as the standard record elimination rule: if a eld x of t has binding x:T, then t.l has type T. If T depends on other elds|that is, if the name Xi (or xi) occurs free in T|then the corresponding record projection t.Xi (or t.xi, resp.) should be substituted for Xi (or xi, resp.) to prevent the eld name from escaping its scope.4 ? ` t : {|Bii21 n|}

Bj =x:T
? ` t.lj : fBV(Bi) 7! t.lii21 j?1gT
3 Strictly speaking, these two mechanisms should be kept separate. In the full typing
rules in Appendix A, each eld is given two names: an external name, which can be
used for projections, and an internal name, which binds the subsequent occurrences
in the record. The simpli ed syntax presented in the body of the paper corresponds
to the special case where the external and internal names are identical.)
4 Experts will note that we give a somewhat simpler version of this rule than Harper
and Lillibridge [

The subtyping rule for record types is: ` ?; B1; : : : ; Bn+k ok ` ?; B01; : : : ; B0n ok

?; B1; : : : ; Bj?1 ` Bj <: B0j j21 n
? ` {|Bii21 n+k|} <: {|B0ii21 n|}
As usual for ordinary (non-dependent) records, \width subtyping" is allowed:
extra elds (the n+1-st to n+k-th elds) can be dropped. Also, corresponding
bindings Bi and B0i are compared using a sub-binding relation. When both are
term bindings|i.e., Bi and B0i are of the form x:S and x:T|S should be a
subtype of T: this captures ordinary \depth subtyping." For type bindings, we
have (X$T) <: (X<:S) <: (X<:U) if T <: S <: U; the rst clause ((X$T) <: (X<:S))
allows the exact identity of a type eld to be replaced with an upper bound;
the second ((X<:S) <: (X<:U)), corresponding to subtyping of bounded
existential types, allows us to loosen the bound of X. For example, we can derive
{|9X$Nat,x:X!X|} <: {|9X:*,x:X!X|}. (As usual, this rule leads to an
undecidable subtyping relation [

For the encoding of classes, we will need to be able to give quite precise types to functions, showing the dependency of the type of the result on the value of the argument.

In outline, the intuition is this. Suppose we write a function cl = [self:{|9T:*, x:T, f:T!T|}]

{T=self.T, x=self.f(self.x), f=self.f}; whose argument is a record containing a type, a value (of that type), and a function (on that type), and whose result is a record with a similar shape, but where the value eld is calculated by applying the argument's function eld to the argument's value eld. The type of this function cl :

[self: {|9T:*,x:T,f:T!T|}] {|9T$self.T, x:T, f:T!T|} expresses the fact that the T eld of the result is identical to the T eld of the argument. Next, suppose we create a record containing these three items r1 = {T=Nat, x=3, f=plusfour} :: {|9T:*, x:T, f:T!T|}; r1 : {|9T:*, x:T, f:T!T|} and use the function cl to obtain another record of the same shape: r2 : {|9T$r1.T, x:T, f:T!T|} Notice that, because of the dependent typing of cl, the type of r2 exposes the fact that it was built from r1|in particular, that their type components are equal. Hence, it is legal to project the function eld from r2 and apply it to the value eld from r1: we obtain no information about the relation between r1's T eld and r2's, and the application r2.f r1.x is not allowed.

In general, a function [x:S]t has type [x:S]T, where x is allowed to appear in T. (When x does not appear in T, we write [x:S]T as S!T, recovering the usual notation for function types as a special case of dependent function types.) The rules for function abstraction and application are generalized accordingly: ? ` [x:S]t : [x:S]T ? ` t : [x:S]T

? ` s : S ? ` t s : fx 7! sgT 4

With the formalities of our typed lambda-calculus now in hand, we can proceed
to the technical heart of the paper: a straightforward encoding of the animal
example from Section 2 in terms of records with type elds. For the sake of
concreteness, we extend the familiar existential encoding of objects [

To avoid introducing additional complexities in the type theory, we give an encoding of purely functional objects; for example, we assume that an animal's eat method returns a new, satiated animal rather than side-e ecting the internals of the receiving animal.

To get warmed up, let's begin with an example that does not involve virtual types: one-dimensional point objects with methods get to retrieve a current coordinate, set to move to a new coordinate, and bump to move a little from the present position.

In the simple existential encoding, the interface of an object is represented as a type operator of the form [Rep:*]{|mi:Tii21 n|}, where the bound variable Rep stands for the hidden type of the object's internal state, and where each Ti is type of the corresponding method mi. Each method takes the internal state of the object as an explicit argument and, if appropriate, returns a new internal state as its result. For example, the interface PointI of point objects is represented as

PointI = [Rep:*] {|get:Rep!Nat, set:Rep!Nat!Rep, bump:Rep!Rep|}; PointI : * ! *

Interfaces for objects with virtual types may include not only methods but also type elds, which declare the bounds of the virtual types. The interface AnimalI is represented as

AnimalI= [Rep:*]{|9FT<:Food, eat:Rep!FT!Rep, eatALot:Rep!FT!Rep|}; The binding FT<:Food is a direct transliteration of the constraint on FT in Section 2. Similarly, the interface CowI is represented as

CowI = [Rep:*]{|9FT$Grass, eat:Rep!FT!Rep, eatALot:Rep!FT!Rep|}; where the binding of FT is now manifest. Note that CowI is a subtype of AnimalI; this will later allow Cow objects to be regarded as animals. 4.2

Intuitively, an object with interface I comprises some hidden internal state, some methods (described by I) that can manipulate that state, and some mechanism for hiding the type of the state from outside view. In the simple existential encoding, an existential quanti er is used to achieve this hiding (it can also be done with recursive types), so the type of our point objects is:

Point = {|9Rep:*, state: Rep,

meth: {|get:Rep!Nat, set:Rep!Nat!Rep, bump:Rep!Rep|}|};

More generally, the type of objects with interface I is a record type including a representation type Rep, a method vector eld containing a record of type I Rep, and a state eld of type Rep. We can capture this structure uniformly by de ning a (higher-order) type operator Object that takes I as a parameter: Object = [I:*!*]{|9Rep:*, meth:I Rep, state:Rep|}; Object : (*!*) ! *

Point = Object PointI; The type Point is now expressed concisely as:

A point object|i.e., an element of type Point|can be constructed \from scratch" as follows (we will see how to create points from classes in Section 4.3): PointR = {|x:Nat|}; point = {9Rep=PointR, meth= fix (PointI Rep) [self:PointI Rep] {get= [s:Rep]s.x, set= [s:Rep] [n:Nat]{x=n}, bump= [s:Rep] self.set s (plus 1 (self.get s))}, state= {x=0}} :: Point; PointR is the concrete representation type of the internal state. The method get just returns the x eld of state, while set returns a new state with the x eld set to its second argument, n. The method bump is de ned in terms of the other methods get and set. In order to access other methods, the record of methods is abstracted on a parameter self of type PointI Rep; the xed-point operator is used to \tie the knot," making self refer to the record itself.

Invocation of the get method of a Point object requires simply extracting the get eld of the object's methods and applying it to the state eld: x = point.meth.get point.state :: Nat; More generally, we can write

get = [p:Point] p.meth.get p.state :: Point ! Nat; for the function that \sends the get message" to an arbitrary point object p.

To send the set and bump messages to point objects, we need to do a little more work: the implementations of these methods return updated copies of just the internal representation, which must then be repackaged with the original methods into complete objects: bump = [p:Point]{9Rep=p.Rep, meth= p.meth,

state= p.meth.bump p.state} :: Point ! Point;

The construction of a Cow object is similar. The only signi cant di erence is that the record of methods includes a type eld FT, which should be given a concrete de nition of food for a cow. Furthermore, methods taking arguments of FT can do grass-speci c operation (such as enoughGrass) to the argument. Choosing the simple representation

CowR = {|hungry:Bool|}; for the internal state of cows, we can de ne an element of the type Object CowI as follows: cow = {9Rep=CowR, meth= fix (CowI Rep) [self:CowI Rep] {9FT=Grass, eat= [s:Rep] [f:FT]

if enoughGrass f then {hungry=false} else s, eatALot= [s:Rep] [f:FT](self.eat (self.eat s f) f)}, state= {hungry=true}} :: Object CowI; Like the bump method of point objects, the eatALot method of cows is de ned by invoking the eat method via the self parameter.

Since we know FT is equal to Grass (by the de nition of CowI), we can feed grass to our cow: feed = [c:Object CowI] [g:Grass]

{9Rep=c.Rep, meth=c.meth, state=c.meth.eatALot c.state g} :: Object CowI ! Grass ! Object CowI; satisfiedCow = feed cow grass :: Object CowI; 4.3

So far, virtual types have presented no special di culties: the encodings of points and cows have been essentially identical. For encoding classes, however, the virtual types lead to some extra complications.

A class is a data structure providing implementations for a collection of methods and abstracted on a self-parameter. Concretely, a class whose instances are objects with interface I is represented as a function taking self as an argument and returning a record of methods of type I R, where R is the representation type of the state. For example, a class of point objects can be de ned as follows: pointClass = [self: PointI PointR] {get= [s:PointR]s.x, set= [s:PointR] [n:Nat]{x=n}, bump= [s:PointR]self.set s (plus 1 (self.get s))} :: PointI PointR!PointI PointR; To build a point object from the point class, we choose some particular representation (some element of type PointR) and calculate its record of methods by taking the xed point of the class: point = {9Rep=PointR, meth=fix (PointI Rep) pointClass, state={x=0}} :: Object PointI;

The fact that the methods of pointClass are abstracted on self allows us to de ne new subclasses of pointClass that inherit some of its behavior. For example, here is a class of colored point objects:

CPointI = [Rep:*] {|get:Rep!Nat, set:Rep!Nat!Rep,

bump:Rep!Rep, color:Rep!Color|}; cpointClass = [self: CPointI PointR] let super = pointClass self in {get=super.get, set=super.set, bump=super.bump, color= [s:PointR] red} :: CPointI PointR ! CPointI PointR; cpoint = {9Rep=PointR, meth=fix (CPointI Rep) cpointClass,

state={x=0}} :: Object CPointI; The superclass's method suite super is obtained by application of pointClass to (cpointClass's) self. Note that, for brevity, we choose the same representation type for both pointClass and cpointClass; it is easy to generalize this so that cpointClass can add new instance variables (such as a color eld), but the extra mechanism would make the examples harder to read.

When virtual types are involved, we need to be a little more precise about the typing of classes. Here, for example, is the de nition of a generic animalClass. (Again, for brevity we use the same representation type (AnimalR) for both animalClass and cowClass.)

AnimalR = {|hungry:Bool|}; animalClass = [self:AnimalI AnimalR] {9FT=self.FT, eat=self.eat, eatALot= [s:AnimalR] [f:FT]self.eat (self.eat s f) f} :: [self: AnimalI AnimalR] {|9FT$self.FT, eat: AnimalR!FT!AnimalR,

eatALot: AnimalR!FT!AnimalR|}; This de nition involves a few subtle points. First, since the type FT and the method eat are virtual, their concrete de nitions cannot be provided. Instead of concrete de nitions, the corresponding elds of self are used. Second, type of animalClass is not AnimalI AnimalR!AnimalI AnimalR, but a dependent function type (a more re ned subtype of AnimalI AnimalR!AnimalI AnimalR). This typing is essential when we derive cowClass from animalClass, as we will see below.

In the de nition of cowClass, the FT and eat elds are lled with their concrete de nitions and the eatALot method is inherited from animalClass. Since cowClass's self is passed to animalClass, self.eat in method eatALot refer to the eat method of cowClass (not the virtual eat method of animalClass.) Now, since FT is not derived from self, the type of cowClass is just a (nondependent) function type.

cowClass = [self:CowI CowR] let super = animalClass self in {9FT=Grass, eat= [s:CowR] [f:FT]

if enoughGrass f then {hungry=false} else s, eatALot=super.eatALot} :: CowI CowR ! CowI CowR;

The dependent function type of animalClass is critical for cowClass to be well-typed: if animalClass had only type AnimalI AnimalR!AnimalI AnimalR, cowClass would be ill-typed since super.eatALot has type CowR!FT!CowR where FT <: Food, which is not a subtype of CowR!Grass!CowR. Thanks to the dependent function type of cowClass, the projection super.eatALot has type CowR!self.FT!CowR, which is exactly equal to CowR!Grass!CowR.

Finally, a cow object can be created by instantiating cowClass in the usual way: cow = {9Rep=CowR, meth=fix (CowI Rep) cowClass,

state={hungry=true}} :: Object CowI;

The \overlap" between virtual types and parametric classes as alternative mechanisms for achieving similar kinds of genericity has been remarked by several authors [5, 34, etc.]. To build a generic Bag class, for example, one can proceed in two ways. On one hand, we can make the type of the bag's elements a (virtual) eld of the Bag class and obtain concrete instances by subclassing the generic Bag class, overriding the member type eld with the actual member type. On the other hand, we can make the element type a parameter to the class de nition, essentially making the class into a polymorphic function, and obtain concrete instances by instantiating this polymorphic function with the actual member type. In this section, we rst compare these two styles by means of a fully worked example, then comment on the general case. The overlap between the styles can be viewed, in terms of our encoding, as a corollary of the inter-de nability of universal and existential polymorphism in the presence of dependent records.

Generic programming was one of the rst applications of virtual types. The typical pattern proceeds in two steps: (1) a generic class with a virtual type is de ned, with generic implementations of its operations in terms of the virtual type; (2) this class is then specialized, overriding the virtual type to some concrete instance. For example, suppose we want to program with homogeneous collections (bags) of objects of some type T. We start by building a generic Bag class with a virtual type E (which stands for type of elements) and implementations of the bag methods (put, get, etc.). Since the representation type of state of bags is parameterized by E, the interface of bags takes a type operator Rep of kind *!*, and the type of the state is actually represented as Rep E.

BagI = [Rep:*!*] {|9E:*, put:(Rep E)!E!(Rep E), get:(Rep E)!E|}; Choosing lists of elements as our internal representation,

BagR = [E:*] {|elts:List(E)|}; we can de ne a generic bag class as follows: bagClass = [self:BagI BagR] {9E=self.E, put= [s:BagR E] [e:E]({elts= cons E e s.elts} ::BagR E), get= [s:BagR E] car E s.elts} :: [self:BagI BagR]

{|9E$self.E, put:BagR E!E!BagR E, get:BagR E!E|}; The next step is to make a subclass with a concrete de nition for the element type. The class natBagClass is de ned by giving the concrete value Nat to the virtual type E and by inheriting all methods from bagClass.

NatBagI = [Rep:*!*]{|9E$Nat,

put:(Rep E)!E!(Rep E), get:(Rep E)!E|}; natBagClass = [self:NatBagI BagR] let super = bagClass self in {9E=Nat, put= super.put, get= super.get} :: NatBagI BagR ! NatBagI BagR; The interfaces and classes here are fairly similar to the examples we saw in Section 4 (modulo the fact that the representation type here is a type operator); the construction of bag objects, however, requires a little explanation. The rst observation is that the hidden state type is now a type operator. (Intuitively, we \see" that the representation of the object may involve the virtual type eld E, but that is all we are allowed to know about the representation.) The second is that the order of the state eld and the meth eld is essential, since the type of the state depends both on Rep and on the E component of the meth. The code for invoking operations on bag objects is adjusted accordingly: sendget = [b:NatBag] b.meth.get b.state :: NatBag!Nat; sendput = [b:NatBag] [e:Nat] {9Rep=b.Rep, meth= b.meth, state= b.meth.put b.state e} :: NatBag!Nat!NatBag;

By contrast, let's look at how bags can be modeled in terms of parametric classes. Instead of the element type being a member of the bag class, it will be a parameter to the class. Similarly, the interface BagI is parameterized by E: BagI = [E:*] [Rep:*] {|put:Rep!E!Rep, get:Rep!E|}; bagClass = [E:*] [self:BagI E (BagR E)] {put= [s:BagR E] [e:E] {elts= cons E e s.elts}, get= [s:BagR E] car E s.elts} :: 8[E:*]BagI E (BagR E)!BagI E (BagR E); Note that bagClass has a polymorphic function type. (Also, note that Rep has kind * now, not *!*, since it is being supplied from the outside and there is no need to apply it to anything in this de nition.)

The concrete instance natBagClass is now de ned by instantiating bagClass with the type parameter Nat.

NatBagI = [Rep:*]{|put:Rep!Nat!Rep, get:Rep!Nat|}; natBagClass = bagClass Nat; A bag object is de ned by instantiating the class in the usual way. (Here there are no subtle dependencies between the type of the meth and state elds.) NatBag = {|9Rep:*, meth:NatBagI Rep, state:Rep|}; natBag = {9Rep=BagR Nat, meth=fix (NatBagI Rep) natBagClass, state= {elts= (nil Nat)}} :: NatBag; sendget = [b:NatBag] b.meth.get b.state :: NatBag!Nat; sendput = [b:NatBag] [e:Nat] {9Rep=b.Rep, meth= b.meth, state= b.meth.put b.state e} :: NatBag!Nat!NatBag;

These examples illustrate the basic di erence between virtual types and parametric classes as mechanisms for generic programming. A parametric class is instantiated by type application, taking the element type directly as an argument. With virtual types, on the other hand, type parameterization is realized by a dependent function whose argument has a type eld in it. Since the get eld depends on self.E, it will have type (List Nat)!Nat when the E eld of the supplied self record has been set to Nat.

This correspondence can be viewed as an instance of a more general observation: polymorphic functions can be encoded in terms of dependent functions on dependent records. A polymorphic abstraction [X<:S]t of type 8[X<:S]T can be represented as the dependent function [x:{|9X<:S|}](tfX 7! x.Xg) of type [x:{|9X<:S|}](TfX 7! x.Xg); it takes an argument {|9X$U|} where U is some subtype of S and behaves as a term of type TfX 7! {|9X$U|}.Xg, which is equal to the type TfX 7! Ug of the corresponding polymorphic application ( [X<:S]t) U. The table below summarizes this encoding:

type abstraction application

8 8[X<:S] T [X<:S] t t T

9 + [x:{|9X<:S|}] (TfX 7! x.Xg) [x:{|9X<:S|}] (tfX 7! x.Xg) t {9X=T} 6

Virtual types (called virtual classes in the original proposal) were rst introduced
in Beta [

AnimalI= [Rep:*]{|9FT$Food, eat:Rep!FT!Rep, eatALot:Rep!FT!Rep|}; where FT is declared equal to Food. However, they also allow type elds to be specialized, so that

CowI = [Rep:*]{|9FT$Grass, eat:Rep!FT!Rep, eatALot:Rep!FT!Rep|}; as before. Finally, they want to regard cows as animals, i.e., CowI <: AnimalI and Object CowI <: Object AnimalI. Taken together, these properties (speci cally, the inclusion CowI <: AnimalI) yield a statically unsafe type system: we can take a cow, regard it as an animal, and feed it some meat (which has type Meat, a subtype of Food, and hence an acceptable argument to an Animal's eat method).

Various approaches have been suggested to remedy this unsoundness. In Beta
and in Thorup's proposed Java extension, run-time checks are added to methods
like eat to make sure that their arguments are actually acceptable. (In [

Torgersen [

A possible criticism of Torgersen's idea is that, in general, it may lead to duplication of the class hierarchy. For one thing, if the class Animal contains virtual types but no virtual methods (i.e., if eat is given a concrete generic implementation), then we may want to instantiate the class Animal itself. This requires making an explicit subclass (let's call it @Animal) of Animal in which FT is equal to Food.

Cow (FT$Grass)

Animal <:? (FT<:F_?o?<o:d??) @Animal

(FT$Food) Also, rather than making Cow a leaf of the subclass hierarchy, we may wish to allow further specialization in subclasses. In this case, we should change the constraint on FT to <:Grass, make Cow a virtual class, and introduce another leaf class @Cow in which FT$Grass.

Cow ? (FT<:Grass) <: BrownCow (FT<:BrownGrass) _?? <:?? _?? <:??

Animal
<:? (FT<:Food)
_??
<:??
@Animal
(FT$Food)
Fortunately, the @ variants can be derived mechanically from the other classes,
as Torgersen himself pointed out in his original paper. More recently, Bruce,
Odersky and Wadler [

Bruce, Odersky, and Wadler also pointed out that virtual types have an
advantage over parametric classes in de ning mutually recursive classes such as
alternating lists or the Subject/Observer pattern [

Recently, Bruce and Vanderwaart [

We have presented a straightforward encoding of objects with virtual types in a fairly standard (though quite powerful) type theory. In our model, objects are expressed as dependent records with manifest and/or bounded type elds; classes are modeled as dependent functions. The overlap between parametric classes and virtual types can then be viewed as a consequence of the encodability of universal polymorphism in terms of existential polymorphism with dependent functions. We are working to extend this encoding in two main directions: { Imperative variants of the encoding, where methods like eat work by sidee ecting mutable instance variables. { Recursive and mutually recursive classes involving virtual types, such as the well-known subject-observer example.

The second of these seems relatively straightforward. The rst, somewhat surprisingly (and disappointingly) does not|the technicalities of the underlying type theory required to achieve soundness when imperative features are combined with dependent types become astonishingly subtle.

An obvious question is whether other type-theoretic encodings of simple
objects|for example, the standard recursive-records encoding [

Another interesting question is whether the type theory in which we are working here is the simplest possible for the task. All of the features described in Section 3|in particular, both dependent records and dependent functions| are used by our encoding, but it is possible that a di erent encoding could get by with less.

Finally, it would be worthwhile to formalize the translation from a highlevel language with virtual types into low-level structures like the ones we have explored here. The interesting point is to see how much of of the type-theoretic complexity of the target language will also show up in the typing rules of the high-level language.

This work was supported by Indiana University, the University of Pennsylvania, and the National Science Foundation under grant CCR-9701826, Principled Foundations for Programming with Objects. Igarashi is a research fellow of the Japan Society for the Promotion of Science.

Discussions with Kim Bruce, Bob Harper, Didier Remy, and Philip Wadler deepened our understanding of this material. Comments from the FOOL and ECOOP referees helped us improve the nal presentation.

A

This appendix summarizes the syntax, reduction rules, and typing rules of the type system used in this paper. All of the individual features of this calculus have been studied carefully in the literature, and their combination here is believed (but has not been proved!) to be sound. All of the examples in the paper have been checked mechanically by our implementation.

A.1

The sets of kinds K, types T, bindings B, terms t, elds , and contexts ? are de ned by the following grammar:

K ::= *

K!K B ::=

X<:T X$T x:T proper types operator kind T ::=

X type variable Top Top type 8[X<:T]T polymorphic func. type [x:T]T dependent func. type [X:K]T type operator T T type op. application {|li/Bii21 n|}

type of records t.l type eld projection bounded type binding manifest type binding term binding t ::= x

variable [X<:T]t polymorphic abstraction [x:T]t abstraction t T polymorphic application t t application {li/ ii21 n}

record intro t.l record projection fix T t xed-point operator

::= X=T x=t ? ::= s.lj?!fBV( i) 7! s.lii21 j?1gt

( [X<:S]t) T?!fX 7! Tgt fix T s?!s (fix T s) ` ? ok ? is a well-formed context ? ` T : K T is a type constructor of kind K ? ` t : T t is a term of type T ? ` : B is a eld of binding B ? ` S <: T S is a subtype of T ? ` B1 <: B2 B1 is a subbinding of B2 ? ` S $ T S and T are equivalent ? ` B1 $ B2 B1 and B2 are equivalent ? ` T : K ? ` T : *

` ? ok ` ? ok

` ok X 62 dom(?) ` ?; X<:T ok x 62 dom(?) ` ?; x:T ok

? ` Top : * X$T 2 ? ? ` T : K ? ` X : K ` ?; X<:S ok ?; X<:S ` T : * ? ` 8[X<:S]T : * ` ?; X:K1 ok ?; X:K1 ` T : K2 ? ` [X:K1]T : K1!K2 ` ?; B1; : : : ; Bn ok ? ` {|li/Bii21 n|} : * x:T 2 ? ? ` x : T ? ` t : 8[X<:T]U ? ` S <: T ? ` t S : fX 7! SgU ` ? ok ` ? ok X 62 dom(?) ` ?; X:K ok X 62 dom(?) ` ?; X$T ok ? ` T : K X:K 2 ? ? ` X : K X<:T 2 ? ? ` T : K ? ` X : K ` ?; X<:S ok ? ` t : S ? ` S <: T ? ` t : T

? ` T : K ? ` (X=T) : (X$T)

? ` S $ T ? ` S <: T X<:T 2 ? ? ` X <: T ` ?; X<:S1 ok ` ?; X<:S2 ok ? ` S2 <: S1 ?; X<:S2 ` T1 <: T2 ? ` 8[X<:S1]T1 <: 8[X<:S2]T2 ` ?; X:K ok ?; X:K ` S <: T ? ` [X:K]S <: [X:K]T ? ` T : K ? ` T $ T ? ` S $ T ? ` T $ U ? ` S $ U ? ` S $ T ? ` (X<:S) $ (X<:T)

? ` S $ T ? ` (x:S) $ (x:T) ` ?; x:S1 ok ? ` S1 $ S2 ?; x:S1 ` T1 $ T2 ? ` [x:S1]T1 $ [x:S2]T2

? ` S1 T1 : K ? ` S1 $ S2 ? ` T1 $ T2

? ` S1 T1 $ S2 T2 ? ` t : {|li/Bii21 n|} Bj=X$T ? ` t.lj $ fBV(Bi) 7! t.lii21:::j?1gT