Compiling polymorphism using intensional type analysis

Traditional techniques for implementing polymorphism use a universal representation for objects of unknown type. Often, this forces a compiler to use universal representations even if the types of objects are known. We examine an alternative approach for compiling polymorphism where types are passed as arguments to polymorphic routines in order to determine the representation of an object. This approach allows monomorphic code to use natural, efficient representations, supports separate compilation of polymorphic definitions and, unlike coercion-based implementations of polymorphism, natural representations can be used for mutable objects such as refs and arrays.
We are particularly interested in the typing properties of an intermediate language that allows run-time type analysis to be coded within the language. This allows us to compile many representation transformations and many language features without adding new primitive operations to the language. In this paper, we provide a core target language where type-analysis operators can be coded within the language and the types of such operators can be accurately tracked. The target language is powerful enough to code a variety of useful features, yet type checking remains decidable. We show how to translate an ML-like language into the target language so that primitive operators can analyze types to produce efficient representations. We demonstrate the power of the “user-level” operators by coding flattened tuples, marshalling, type classes, and a form of type dynamic within the language.


Introduction
Many compilers assume a universal or \boxed" representation of a single machine word if the type of a value is unknown.This allows the compiler to generate one simple piece of code to manipulate the value.But boxed representations often require more space and provide less e cient access than natural representations.For example, an array of small unknown objects, such as booleans or characters, is represented as an array of words, wasting the majority of the space.An object larger than a word, such as a doubleprecision oating-point value, is allocated and a pointer is used in place of the value.Consequently, accessing the value requires an additional memory access.As word sizes increase from 32 to 64-bits, and memory latencies increase, it becomes increasingly important to minimize boxing.
In modern programming languages such as Modula-3, Standard ML (SML), and Haskell, unknown types and thus boxed representations arise because of two key language features: types imported from a separately compiled program unit and types within polymorphic routines.Polymorphic values are particularly troublesome because we can simultaneously view them as having any one of an in nite number of monomorphic types.For example, a polymorphic routine that maps a function across the elements of an array can be simultaneously seen as a function that works on boolean arrays and a function that works on real arrays.The routine can thus be used in place of a function that was compiled knowing whether the argument array contains booleans or reals.Consequently, monomorphic routines are forced to use the same representations as polymorphic routines and the entire program pays the price of the increased space and execution-time overheads of the universal representations.

Coercion Implementations
The problem with polymorphism stems from the assumption that viewing a polymorphic value as a monomorphic value should have no computational e ect.Recent work by Leroy 30] and others 41,24,43] has suggested that the instantiation of a polymorphic value should correspond to a run-time coercion from the universal representation to the appropriate specialized representation.At function types, this requires the dual coercion (for the function argument) that converts specialized representations to the universal representation.For example, when the identity function of type 8 : ! is instantiated to have type int !int, a coercion is generated that takes an integer argument, boxes it, passes it to the identity function, and unboxes the result.This approach allows monomorphic code to use the natural, e cient representations.
Leroy's coercions produce an isomorphic copy of a data structure.For example, to coerce a tuple, we project the components of the tuple, box/unbox them, and then form a new tuple.Unfortunately, copying coercions are impractical for large data structures since the cost of making the copy often outweighs the bene ts of the unboxed representation (as pointed out by Leroy 30,page 184]).More problematically, copying coercions do not work for mutable data structures such as arrays.If we make a copy of the value to box the components then updates to the copy will not be re ected in the original array and vice versa.

Type Passing
An alternative approach to coercions, rst suggested by the Napier '88 implementation 37], is to pass the types that are unknown at compile-time to primitive operations at linktime or even run-time.Then the primitive operations can analyze the type in order to select the appropriate code to manipulate the natural representation of an object.For example, a polymorphic subscript function for arrays might be compiled into the following pseudo-code: sub = :typecase of bool ) boolsub j real ) realsub j ) boxedsub ] Here, sub is a function that takes a type argument ( ), and does a case analysis to determine the appropriate specialized subscript function that should be returned.For example, sub bool] returns the boolean subscript function that expects an array of bits, while sub real] returns the oating point subscript function that expects a double-word aligned array of oating point values.For all other types, we assume the array has boxed components and thus return the boxed subscript function.
If the sub operation is instantiated with a type that is known at compile-time (or link-time), then the overhead of the case analysis can be eliminated by duplicating and specializing the de nition of sub at the appropriate type.For example, the source expression \subhx; 4i + 3:14" will be compiled to the target expression \sub real]hx; 4i + 3:14" since the result of the sub operation is constrained to be a real.If the de nition of sub is inlined into the target expression and some simple reductions are peformed, this yields the optimized expression \realsubhx; 4i+3:14".Thus, parameterizing the primitive operations by type provides a single, consistent methodology for type analysis at compiletime, link-time, and run-time.
In languages where polymorphic de nitions are restricted to \computational values" (essentially constants and functions), polymorphic de nitions can always be duplicated and specialized or even inlined.Lazy languages such as Haskell satisfy this constraint, and Wright has determined empirically that such a restriction does not e ect the vast majority of SML programs 52].Languages like core-SML and Haskell only allow polymorphic values to arise as the result of a \let" binding and restrict the type of such values to be prenex-quanti ed.That is, the type must be of the form 8 1; : : : ; n: where contains no quanti er.Thus, the only thing that can be done to a polymorphic value is to instantiate it.Since the scope of a let is closed, it is possible to determine all of the instantiations of the polymorphic value at compile time and eliminate all polymorphism through duplication andd specialization.Such an approach is used, for instance, by Blelloch et al. in their NESL compiler 6] and more recently by Jones to eliminate Haskell overloading 27].Furthermore, Jones reports that this approach does not lead to excessive code-blowup.
Unfortunately, eliminating all of the polymorphism in a program is not always possible or pratical.In particular, there is no way to eliminate the polymorphism when separately compiling a de nition from its uses because it is impossible to determine the types at which the de nition will potentially be used.This prevents us from separately compiling polymorphic libraries or polymorphic de nitions entered at a top-level loop.Furthermore, in languages that allow polymorphic values to be \ rst-class" such as XML 21] and Quest 9], it is impossible to eliminate all polymorphism at compile-time.Therefore, we view duplication and specialization as an important optimization, but consider some run-time type analysis to still be necessary for practical language implementation.

Type-Checking Type Analysis
In this paper, we show how to compile ML-like polymorphic languages to a target language where run-time type analysis may be used by the primitive operations to determine the representation of a data structure.We are particularly interested in the typing properties of a language that allows run-time type analysis.The sub de nition above is ill-typed in ML because it must simultaneously have the types boolarray int !bool, realarray int !real, as well as 8 :( )boxedarray int ! .Since boolarray and realarray are nullary constructors and not instantiations of ( )boxedarray, it is clear that there is no ML type that uni es all of these types.
Our approach to this problem is to consider a type system that provides analysis of types via a type-level \Typecase" construct.For example, the sub de nition above can be assigned a type of the form: where the specialized array constructor SpclArray is de ned using Typecase as follows: SpclArray ] = Typecase of bool ) boolarray j real ) realarray j ) ( )boxedarray The de nition of the constructor parallels the de nition of the term: If the parameter is instantiated to bool, then the resulting type is boolarray and if the parameter is instantiated to real, the resulting type is realarray.
In its full generality, our target language allows types to be analyzed not just by case analysis, but also via primitive recursion.This allows more sophisticated transformations to be coded within the language, yet type checking for the target language remains decidable.An example of a more sophisticated translation made possible by primitive recursion is one where arrays of tuples are represented as tuples of arrays.For example, an array of bool real is represented as a pair of a boolarray and a realarray.This representation allows the boolean components of the array to be packed and allows the real components to be naturally aligned.The subscript operation for this representation is de ned using a recursive typecase construct called typerec in the following manner: typerec sub bool] = boolsub j sub real] = realsub j sub 1 2] = hhx; yi; ii:hsub 1] hx;ii; sub 2] hy; iii j sub ] = boxedsub ] If sub is given a product type, 1 2, it returns a function that takes a pair of arrays (hx; yi) and an index (i) and returns the pair of values from both arrays at that index, recursively calling the sub operation at the types 1 and 2.
The type of this sub operation is: Run-time type analysis can be used to provide other useful language mechanisms besides e cient representations.In particular, ad hoc polymorphic operators, such as the equality operator of SML, or an overloaded operator exported from a Haskell type class, can be directly implemented in our target language without the need to tag values.Furthermore, the static constraints of SML's equality types and Haskell's type classes may be coded using our Typerec construct.Our target language is also able to express \marshalling" of data structures and a form of type dynamic.
In Section 2 we describe the type-analysis approach to compilation as a type-based translation from a source language, Mini-ML, to our target language, ML i .The key properties of ML i are stated, and a few illustrative examples involving typerec and Typerec are given.In Section 3 we show how many interesting and useful language constructs can be coded using typerec, including attened representations, marshalling, type classes, and type dynamic.In Section 4 we discuss related work, and in Section 5 we summarize and suggest directions for future research.

Type-Directed Compilation
In order to take full advantage of type information during compilation, we consider translations of typing derivations from the implicitly-typed ML core language to an explicitlytyped target language, following the interpretation of polymorphism suggested by Harper and Mitchell 20].The source language is based on Mini- ML 11], which captures many of the essential features of the ML core language.The target language, ML i , is an extension of ML , also known as XML 21], a predicative variant of Girard's F! 16,17,42], enriched with primitives for intensional type analysis.

Source Language: Mini-ML
The source language for our translations is a variant of Mini- ML 11].The syntax of Mini-ML is de ned by the following grammar: (monotypes) ::= t j int j 1 ! 2 j 1 2 (polytypes) ::= j 8t: (terms) e ::= x j n j he1; e2i j 1 e j 2 e j x: e j e1 e2 j let x = v in e (values) v ::= x j n j hv1; v2i j x: e Monotypes ( ) are either type variables (t), int, arrow types, or binary product types.Polytypes ( ) (also known as type schemes) are either monotypes or prenex quanti ed types.
The terms of Mini-ML (e) consist of identi ers, numerals ( n), pairs, rst and second projections, abstractions, applications, and let expressions.Values (v) are a subset of the terms and include identi ers, integer values, pairs of values, and abstractions.
The static semantics for Mini-ML is given in Figure 1 as a series of inference rules.The rules allow us to derive a judgement of the form ; .e : where is a set of free type variables and is a type assignment mapping identi ers to polytypes.We write =t] 0 to denote the substitution of the type for the type variable t in the type expression 0 .
We use ] 0 to denote the union of two disjoint sets of type variables, and 0 , and ]fx : g to denote the type assignment that extends so that x is assigned the polytype , assuming x does not occur in the domain of .
Let-bound expressions are restricted to values so that our translation, which makes type abstraction explicit, is correct (see below).

Target Language: ML i
The target language of our translations, ML i , is based on ML 20], a predicative variant of Girard's F! 16,17,42].The essential departure from the impredicative systems of Girard and Reynolds is that the quanti er 8t: ranges only over \small" types, or \monotypes", which do not include the quanti ed types.This calculus is su cient for the interpretation of ML-style polymorphism (see Harper and Mitchell 20] for further discussion of this point.)The language ML i extends ML with intensional (or structural 19]) polymorphism, that allows non-parametric functions to be de ned by intensional analysis of types.
The o cial syntax of terms shows that the primitive operations of the language are provided with type information that may be used at run-time.For example, the pairing operation is he1; e2i1 ; 2 , where ei : i, re ecting the fact that there is (potentially) a pairing operation at each pair of types.In a typical implementation, the pairing operation is implemented by computing the size of the components from the types, allocating a suitable chunk of memory, and copying the parameters into that space.However, there is no need to tag the resulting value with type information because the projection operations ( 1 ; 2 i e) are correspondingly indexed by the types of the components so that the appropriate chunk of memory can be extracted from the tuple.Similarly, the application primitive (@ e1 e2) is indexed by the domain type of the function 1 and is used to determine the calling sequence for the function.Of course, primitive operations can ignore the type if a universal representation is used.Consequently, the implementor can decide whether to use a natural or universal representation.We use a simpli ed term syntax without the types when the information is apparent from the context.However, it is important to bear in mind that the type information is present in the fully explicit form of the calculus.
The Typerec and typerec forms provide the ability to de ne constructors and terms by structural induction on monotypes.These forms may be thought of as eliminatory forms for the kind at the constructor and term level.(The introductory forms are the constructors of kind ; there are no introductory forms at the term level in order to preserve the phase distinction 8, 21].)At the term level typerec may be thought of as a generalization of typecase that provides for the de nition of a term by induction on the structure of a monotype.At the constructor level Typerec provides a similar ability to de ne a constructor by induction on the structure of a monotype.
The static semantics of e is a term of type The formation rules for constructors are largely standard, with the exception of the Typerec form: . :: . i :: . ! :: !!!! .:: !!!! .Typerec of ( i j !j ) :: The whole constructor has kind if the constructor to be analyzed, , is of kind (i.e., a monotype), i is of kind , and ! and are each of kind !!!! .
The constructor equivalence rules (see Figure 2) axiomatize de nitional equality 47,31] of constructors to consist of -conversion together with recursion equations governing the Typerec form.Conceptually, Typerec selects i , , or !according to the head-constructor of the normal form of and passes it the components of and the \unrolling" of the Typerec on the components.The level of constructors and kinds is a variation of G odel's T 18].Every constructor, , has a unique normal form, NF( ), with respect to the obvious notion of reduction derived from the equivalence rules of Figure 2 47].This reduction relation is con uent, from which it follows that constructor equivalence is decidable 47].
; .e i : Int=t] ; .e! : 8t1; t2:: : t1=t] !t2=t] !!(t1; t2)=t] ; .e : 8t1; t2:: : t1=t] !t2=t] !(t1; t2)=t] ; .typerec of t: ](e i je!je ) : =t] The argument constructor must be of kind , and the result type of the typerec expression is determined as a function of the argument constructor, namely the substitution of for t in the type expression .The \ t: ]" label provides the type information needed to check the construct without infererence.Typically the constructor variable t occurs in as the argument of a Typerec expression so that =t] is determined by a recursive analysis of .Similar to normalization of a Typerec constructor, the evaluation of a typerec expression selects e i , e , or e! according to the head constructor of the normal form of and passes it the components of and the \unrolling" of the typerec on the components.
Type checking for ML i reduces to equivalence checking for types and constructors.In view of the decidability of constructor equivalence, we have the following important result: Proposition 2.1 It is decidable whether or not ; .e : is derivable in ML i .
To x the interpretation of typerec, we specify a call-byvalue, natural semantics for ML i as a relation of the form e , ! v where v is a closed (with respect to both type and value variables), syntactic value.Values are derived from the following grammar: v ::= n j x: : e j hv2; v2i 1 ; 2 j t:: : e Type abstractions are values, re ecting the fact that evaluation does not proceed under .
Figure 3 de nes the evaluation relation with a series of axioms and inference rules.The semantics uses an auxiliary judgment, , ! 0 , (not formally de ned here) that determines the normal forms of constructors.During evaluation, we only need to determine normal forms of closed constructors of kind .This amounts to evaluating constructors of the form Typerec(:::) and ( 1 2]) by orienting the equivalences of Figure 2 to the right and adding the appropriate congruences.
The rest of the semantics is standard except for the evaluation of a typerec expression which proceeds as follows: First, the normal form of the constructor argument is determined.Once the normal form is determined, the appropriate subexpression is selected and applied to any argument constructors.The resulting function is in turn applied to the \unrolling" of the typerec at each of the argument constructors.Some simple examples using typerec may be found at the end of this subsection.
The semantics uses meta-level substitution of closed values for variables and closed constructors for type variables.
In a lower-level semantics where substitution is made explicit, an environment would be needed not only for value variables, but also for type variables.Tolmach 51] describes many of the issues involved in implementing such a language.Proposition 2.2 (Type Preservation) If ;; ; .e : and e , ! v, then ;; ; .v : .By inspection of the value typing rules, only appropriate values occupy appropriate types and thus evaluation will not \go wrong".In particular, it is possible to show that when evaluating well-typed programs, we only use the proj evaluation rule when 0 .Furthermore, programs written in pure ML i (i.e., without general recursion operators or recursive types) always terminate.Note that in a parametric setting this type contains only constant functions.
To simplify the presentation we usually de ne terms such as zero and sizeof using recursion equations, rather than as a typerec expression.The de nitions of zero and sizeof are given in this form as follows: sizeof Whenever a de nition is presented in this form we tacitly assert that it can be formalized using typerec.

Translating Mini-ML into ML i
A compiler from Mini-ML to ML i is speci ed by a relation ; .es : ) et that carries the meaning that ; .es : is a derivable typing in Mini-ML and that the translation of the source term es determined by that typing derivation is the ML i expression et.Since the translation depends upon the typing derivation, it is possible to have many di erent translations of a given expression.However, all of the translation schemes we consider are coherent in the sense that any two typing derivations produce observationally equivalent translations 7, 26, 20]. 2ere, we give a straightforward compiler whose purpose is to make types explicit so that the primitive operations such as pairing and projection can potentially analyze their types at run-time.This simple translation does not utilize typerec or Typerec, but subsequent translations take advantage of these constructs.
We begin by de ning a translation from Mini-ML types to ML i constructors, written j j: jtj = t jintj = Int j 1 !2j = !(j1j; j 2j) j 1 2j = (j 1j; j 2j) The translation is extended to map Mini-ML type schemes to ML i types as follows: j js = T (j j) j8t: js = 8t:: :j js Finally, we write j j for the kind assignment mapping t to the kind for each t 2 , and j j for the type assignment mapping x to j (x)js for each x 2 dom( ).Proposition 2.4 The type translation commutes with substitution: j n=tn]( ( 1=t1] ) )j = j nj=tn]( ( j 1j=tn]j j) ) The term translation is given in Figure 4 as a series of inference rules that parallel the typing rules for Mini-ML.The var rule turns Mini-ML implicit instantiation of type variables into ML i explicit type application.Operationally, this corresponds to passing the types to the polymorphic value at run-time.The let rule makes the implicit type abstraction of the bound expression explicit.The translation of -abstraction, application, pairing, and projection is straightforward except that these primitive operations are labelled with their types.
The translation may be characterized by the following type preservation property.Theorem 2.5 If ; .e : ) e 0 , then j j; j j .e 0 : j j.
In the presence of computational e ects such as nontermination, if we did not restrict the bound expression in a let to be a value, then the translation would be incorrect since evaluation in ML i does not proceed underabstractions.In other words, the expression might diverge (or print \hello") while its translation would not.

Applications of Type Analysis
In this section, we show how to implement a variety of useful and interesting constructs by extending the simple translation from Mini-ML to ML i to take advantage of typerec and Typerec.We have already shown how simple operations like sizeof and zero can be de ned in ML i .These operations can be exported directly to Mini-ML as constants of the appropriate type.In the following subsections, we de ne new operations that may be exported to Mini-ML and modify the standard translation to change the representation of various types.

Flattening
We consider the \ at" representation of Mini-ML tuples in which nested tuples are represented by a sequence of \atomic" values (for the present purposes, any non-tuple is regarded as \atomic").To simplify the development we give a translation in which nested binary tuples are represented in right-associated form, so that, for example, the Mini-ML type (int int) int will be compiled to the ML i type int (int int).The compilation makes use of intensional type analysis at both the term and constructor levels.
We begin by modifying the type translation on Mini-ML tuples: Here Prod is a constructor of kind !!de ned and the equation The term translation is modi ed by changing the behavior of the pair and rules: ; .e1 : 1 ) e 0 1 ; .e2 : 2 ) e 0 2 ; .he1; e2i : 1 2 ) mkpair j 1j] j 2j] e 0 1 e 0 2 ; .e : 1 2 ) e 0 ; .i e : i ) proj i j 1j] j 2j] e 0 (i = 1; 2) The modi ed translation makes use of three auxiliary functions, mkpair, proj 1 and proj 2 , with the following types: mkpair : 8t1; t2 :: :T (t1) !T (t2) !T (Prod t1] t2]) proj 1 : 8t1; t2 :: :T (Prod t1] t2]) !T (t1) proj 2 : 8t1; t2 :: :T (Prod t1] t2]) !T (t2) The mkpair operation is de ned as follows, using the \unofcial" syntax of the language: The veri cation that mkpair has the required type proceeds by case analysis on the form of its rst argument, relying on the de ning equations for Prod.For example, we must check that mkpair Int] ] has type which follows from the de nition of mkpair Int] ] and the fact that T (Prod Int] ]) int T ( ): Similarly, we must check that mkpair ( a; b )] ] has type T ( ( a; b )) !T ( ) ! T (Prod ( a; b )] ] which follows from its de nition, the derivability of the equation The operations proj 1 and proj 2 are de ned as follows: The veri cation that these constructors have the required type is similar to that of mkpair, keeping in mind the equations governing T ( ) and Prod ] ].
One advantage of controlling data representation in this manner is that it becomes possible to support a type-safe form of casting that we call a view.Let us de ne two Mini-ML types 1 and 2 to be similar, 1 2, i they have the same representation | i.e., i j 1j is de nitionally equivalent to j 2j in ML i .If 1 2, then every value of type 1 is also a value of type 2, and vice-versa.For example, in the case of the right-associative representation of nested tuples above, we have that 1 2 i 1 and 2 are equivalent modulo associativity of the product constructor, and a value of a (nested) product type is a value of every other association of that type.
In contrast to coercion implementations of type equivalence, such an approach to views is compatible with mutable types (i.e., arrays and refs) in the sense that 1 ref is equivalent to 2 ref i 1 is equivalent to 2. This means that we may freely intermingle updates with views of complex data structures, capturing some of the expressiveness of C casts without sacri cing type safety.
The right-associated representation does not capture all aspects of \ atness".In particular, access to components is not constant time, given a standard implementation of the pairing and projection operations.This may be overcome by extending ML i with n-tuples (tuples of variable arity), and modifying the interpretation of the product type appropriately.A rigorous formulation of the target language extended with n-tuples is tedious, but appears to be straightforward.

Marshalling
Ohori and Kato give an extension of ML with primitives for distributed computing in a heterogeneous environment 39].Their extension has two essential features: one is a mechanism for generating globally unique names (\handles" or \capabilities") that are used as proxies for functions provided by servers.The other is a method for representing arbitrary values in a form suitable for transmission through a network.Integers are considered transmissible, as are pairs of transmissible values, but functions cannot be transmitted (due to the heterogeneous environment) and are thus represented by proxy using unique identi ers.These identi ers are associated with their functions by a name server that may be contacted through a primitive addressing scheme.In this section we sketch how a variant of Ohori and Kato's representation scheme can be implemented using intensional polymorphism.
To accommodate Ohori and Kato's primitives the ML i language is extended with a primitive constructor Id of kind ! and a corresponding type constructor id( ), linked by the equation T (Id ]) id(T ( )).The Typerec and typerec primitives are extended in the obvious way to account for constructors of the form Id ].For example, the following constructor equivalence is added: . :: . i :: . !; :: !!!! .id :: !!Typerec Id ] of ( i j !j j id ) id (Typerec of ( i j !j j id )) The primitives newid and rpc are added with the following types: newid : 8t1; t2:: :(T (Tran t1]) !T (Tran t2])) !T (Tran !(t1; t2)]) rpc : 8t1; t2:: :(T (Tran !(t1 where Tran is a constructor coded using Typerec as follows: Tran ( 1; 2)] = (Tran 1]; Tran 2]) Tran Id ]] = Id ] The constructor Tran ] maps to a constructor where each arrow is wrapped by an Id constructor.Thus, values of type T (Tran ]) do not contain functions and are therefore transmissible.It is easy to check that Tran is a constructor of kind ! .
From an abstract perspective, newid maps a function on transmissible representations to a transmissible representation of the function and rpc is its (left) inverse.Operationally, newid takes a function between transmissible values, generates a new, globally unique identi er and tells the name server to associate that identi er with the function on the local machine.For example, the unique identi er might consist of the machine's name paired with the address of the function.The rpc operation takes a proxy identi er of a remote function, and a transmissible argument value.The name server is contacted to discover the remote machine where the value actually lives.The argument value is sent to this machine, the function associated with the identi er is applied to the argument, and the result of the function is transmitted back as the result of the operation.
The compilation of Ohori and Kato's distribution primitives into this extension of ML i relies critically on a \marshalling" operation M that converts a value to its transmissible representation and an \unmarshalling" operation U that converts a value from its transmissible representation.The types of these operations can be easily expressed in terms of Tran: M : 8t:: :T (t) !T (Tran t]) U : 8t:: :T (Tran t]) !T (t) The operations themselves can be de ned as follows using the uno cial syntax of typerec: 3M Int] = x:int:x M !( 1; 2)] = f:T (!( 1; 2)): U Id ]] = x:T (Id ]):x At arrow types, M converts the function to one that takes and returns transmissible types and then allocates and associates a new identi er with this function via newid.Correspondingly, U takes an identi er and a marshalled argument, performs an rpc on the identi er and argument, takes the result and unmarshalls it.
The M and U functions are used in the translation of client phrases that import a server's function and in the translation of server phrases that export functions.The reader is encouraged to consult Ohori and Kato's paper 39] for further details.

Type Classes
The language Haskell 25] provides the ability to de ne a class of types with associated operations called methods.The canonical example is the class of types that admit equality (also known as equality types in SML 33,19]).
Consider adding a distinguished type void (with associated constructor Void) to ML i in such a way that void is \empty".That is, no closed value has type void.We can encode a type class de nition by using Typerec to map types in the class to themselves and types not in the class to void.In this fashion, Typerec may be used to compute a predicate (or in general an n-ary relation) on types.De nitional equality can be used to determine membership in the class.
For example, the class of types that admit equality can be de ned using Typerec as follows: Eq :: !
Eq Int] = Int Eq Bool] = Bool Eq ( 1; 2)] = (Eq 1]; Eq 2]) Eq !( 1; 2)] = Void Eq Void] = Void Here, Eq serves as a predicate on types in the sense that a non-Void constructor is de nitionally equal to Eq ] only if is a constructor that does not contain the constructor !( ; ).
The equality method can be coded using typerec as follows, where we assume primitive equality functions for int and bool and omit some type labels for simplicity: eq Int] = eqint eq Bool] = eqbool eq ( 1; 2)] = x: y:eq Eq 1]]( 1 x)( 1 y) and eq Eq 2]]( 2 x)( 2 y) eq !( 1; 2)] = x:void: y:void:false eq Void] = x:void: y:void:false It is straightforward to verify that: eq : 8t:: :T (Eq t]) !T (Eq t]) !bool Consequently, eq ] e1 e2 can be well typed only if e1 and e2 have types that are de nitionally equal to T (Eq ]).The encoding is not entirely satisfactory because eq !( 1; 2)] can be a well-typed expression.However, the function resulting from evaluation of this expression can only be applied to values of type void.Since no such values exist, the function can never be applied.

Dynamics
In the presence of intensional polymorphism a predicative form of the type dynamic 2] may be de ned to be the existential type 9t:: :T (t).The typing rules for existential types are as follows: ] ft:: g . .:: ; .e : =t] ; .pack e with as 9t:: : : 9t:: : .; .e1 : 9t:: : 0 ] ft:: g; ] fx: 0 g .e2 : ; .abstype e1 is t:: ; x: 0 in e2 end : The pack operation introduces existentials by packaging a type with a value.The abstype operation eliminates existentials by allowing the type and value to be unpacked and used within a certain scope.
Under this interpretation, the introductory form dynamic ](e) stands for pack e with as 9t:: :T (t).The eliminatory form, typecase d of (e i je!je ), where d : dynamic, e i : , and e!; e : 8t1; t2:: : , is de ned as follows: abstype d is t:: ; x:T (t) in typerec t of t: ](e i je 0 !je 0 ) end Here e 0 != t1:: : t2:: : x1: : x2: :e! t1] t2], and similarly for e 0 .This form of dynamic type only allows values of monomorphic types to be made dynamic, consistent with the separation between constructors and types in ML i .The possibilities for enriching ML i to admit impredicative quanti ers (and hence account for the full power of dynamic typing including non-termination) are discussed in the conclusion.
There are two traditional interpretations of polymorphism, the explicit style (due to Girard 16,17] and Reynolds 42]), in which types are passed to polymorphic operations, and the implicit style (due to Milner 32]), in which types are erased prior to execution.In their study of the type theory of Standard ML Harper and Mitchell 20] argued that an explicitly-typed interpretation of ML polymorphism has better semantic properties and scales more easily to cover the full language.Harper and Mitchell formulated a predicative type theory, XML, a theory of dependent types augmented with a universe of small types, adequate for capturing many aspects of Standard ML.This type theory was rened by Harper, Mitchell, and Moggi 21], and provides the basis for this work.The idea of intensional type analysis exploited here was inspired by the work of Constable 12,13], from which the term \intensional analysis" is taken.The rules for typerec, and the need for Typerec, are derived from the \universe elimination" rules in NuPRL (described only in unpublished work of Constable).
The idea of passing types to polymorphic functions is exploited by Morrison et al. 37] in the implementation of Napier '88.Types are used at run-time to specialize data representations in roughly the manner described here.The authors do not, however, provide a rigorous account of the type theory underlying their implementation technique.The work of Ohori on compiling record operations 38] is similarly based on a type-passing interpretation of polymorphism, and was an inspiration for the present work.Ohori's solution is ad hoc in the sense that no general type-theoretic framework is proposed, but many of the key ideas in his work are present here.Jones 28] has proposed a general framework for passing data derived from types to \qualied" polymorphic operations, called evidence passing.His approach di ers from ours in that whereas we pass types to polymorphic operations, that are then free to analyze them, Jones passes code corresponding to a proof that a type satis es the constraints of the quali cation.From a practical point of view it appears that both mechanisms can be used to solve similar problems, but the exact relationship between the two approaches is not clear.
Recently Duggan and Ophel 14] and Thatte 50] have independently suggested semantics for type classes that are similar in spirit to our proposal.In particular both approaches represent the restriction of a class as a user-de ned, possibly recursive, kind de nition in a predicative language.Both sets of authors are concerned with providing a sourcelevel overloading facility and consequently examine hard issues such as type inference and open-scoped de nitions that do not directly concern us, since we are primarily concerned with a target-level type-analysis facility.The implementation technique proposed by Duggan and Ophel is similar to ours in that polymorphic routines are passed type names at run-time and a typecase construct is used to determine the behavior of an overloaded operation.As with type classes and Jones's quali ed types, it appears that we can code many of their kind de nitions using Typerec with the approach sketched in Section 3.3.However, Typerec can also be used to transform types { a facility crucial for representation transformations such as attening and marshalling.That is, neither Duggan and Ophel nor Thatte provide a facility for coding constructors such as Prod or Tran that map types to types.
A number of authors have considered problems pertain-ing to representation analysis in the presence of polymorphism.The boxing interpretation of polymorphism has been studied by Peyton Jones and Launchbury 29], by Leroy 30], by Poulsen 41], by Henglein and J rgensen 24], and by Shao 43] with the goal of minimizing the overhead of boxing and unboxing at run-time.All but the rst of these approaches involve copying coercions.Of a broadly similar nature is the work on \soft" type systems 3,10,23,49,53] that seek to improve data representations through global analysis techniques.All of these methods are based on the use of program analysis techniques to reduce the overhead of box and tag manipulation incurred by the standard compilation method for polymorphic languages.Many (including the soft type systems, but not Leroy's system) rely on global analysis for their e ectiveness.In contrast we propose a new approach to compiling polymorphism that a ords control over data representation without compromising modularity.Finally, a type-passing interpretation of polymorphism is exploited by Tolmach 51] in his implementation of a tagfree garbage collection algorithm.Tolmach's results demonstrate that it is feasible to build a run-time system for ML in which no type information is associated with data in the heap 4 .Morrisett, Harper, and Felleisen 36] give a semantic framework for discussing garbage collection, and provide a proof of correctness of Tolmach's algorithm.

Summary and Future Directions
We have presented a type-theoretic framework for expressing computations that analyze types at run-time.The key feature of our framework is the use of structural induction on types at both the term and type level.This allows us to express the typing properties of non-trivial computations that perform intensional type analysis.When viewed as an intermediate language for compiling ML programs, much of the type analysis in the translations can be eliminated prior to run-time.In particular, the prenex quanti cation restriction of ML ensures good binding-time separation between type arguments and value arguments and the \value restriction" on polymorphic functions, together with the well-foundedness of type induction, ensures that a polymorphic instantiation always terminates.This provides important opportunities for optimization.For example, if a type variable t occurring as the parameter of a functor is the subject of intensional type analysis, then the typerec can be simpli ed when the functor is applied and t becomes known.Similarly, link-time specialization is possible whenever t is de ned in a separately-compiled module.Inductive analysis of type variables arising from let-style polymorphism is ordinarily handled at run-time, but it is possible to expand each instance and perform type analysis in each case separately.
The type theory considered here extends readily to inductively de ned types such as lists and trees.However, extending typerec and Typerec to handle generally recursive types is problematic because of the negative occurrence of in a recursive constructor.In particular, termination can no longer be guaranteed, which presents problems not only for optimization but also for type checking.
The restriction to predicative polymorphism is su cient for compiling ML programs.More recent languages such as Quest 9] extend the expressive power to admit impredicative polymorphism, in which quanti ed types may be instantiated by quanti ed types.(Both Girard's 16] and Reynolds's 42] calculi exhibit this kind of polymorphism.)It is natural to consider whether the methods proposed here may be extended to the impredicative case.Since the universal quanti er may be viewed as a constant of kind ( ! ) ! , similar problems arise as for recursive types.In particular, we may extend type analysis to the quanti ed case, but only at the expense of termination, due to the negative occurrence of in the kind of the quanti er.Ad hoc solutions are possible, but in general it appears necessary to sacri ce termination guarantees.
Compiling polymorphism using intensional type analysis enables data representations that are impossible using type-free techniques.Setting aside the additional expressiveness of the present approach, it is interesting to consider the performance of a type-passing implementation of ML as compared to the type-free approach adopted in SML/NJ 5].As pointed out by Tolmach 51], a type-passing implementation need not maintain tag bits on values for the sake of garbage collection.The only remaining use of tag bits in SML/NJ is for polymorphic equality, which can readily be implemented using intensional type analysis.Thus tag bits can be eliminated, leading to a considerable space savings.On the other hand, it costs time and space to pass type arguments at run-time, and it is not clear whether type analysis is cheaper in practice than carrying tag bits.An empirical study of the relative performance of the two approaches is currently planned by the second author, and will be reported elsewhere.
The combination of intensional polymorphism and existential types 35] raises some interesting questions.On the one hand, the type dynamic 2] may be de ned in terms of existentials.On the other hand, data abstraction may be violated since a \client" of an abstraction may perform intensional analysis on the abstract type, which is replaced at run-time by the implementation type of the abstraction.This suggests that it may be advantageous to distinguish two kinds of types, those that are analyzable and those that are not.In this way parametricity and representation independence can be enforced by restricting the use of type analysis.
The idea of intensional analysis of types bears some resemblance to the notion of re ection 44,4] | we may think of type-passing as a \rei cation" of the meta-level notion of types.It is interesting to speculate that the type theory proposed here is but a special case of a fully re ective type theory.The re ective viewpoint may provide a solution to the problem of intensional analysis of recursive and quanti ed types since, presumably, types would be rei ed in a syntactic form that is more amenable to analysis | using rst-order, rather than higher-order, abstract syntax.

Figure 2 :
Figure 2: Constructor Equivalence only use the app rule when 0

Proposition 2 . 3 (
Termination) If e is an expression such that ;; ; .e : , then there exists a value v such that e , ! v.A few simple examples will help to clarify the use of typerec.The function sizeof of type 8t:: :int that computes the \size" of values of a type can be de ned as follows.sizeof = t:: :typerec t of t 0 :int](e i je!je ) where e i = 1 e! = t1:: : t2:: : x1:int: x2:int: 1 e = t1:: : t2:: : x1:int: x2:int:x1 + x2 (Here we assume that arrow types are boxed and thus have size one.)It is easy to check that sizeof has the type 8t:: :int.
ML i consists of a collection of rules for deriving judgements of the following forms, where is a kind assignment, mapping type variables (t) to kinds, and is a type assignment, mapping term variables to types.