Polymorphism in Hindley/Milner Style Type Systems with Constraints Martin Sulzmann Yale University Department of Computer Science New Haven, CT 06520-8285
[email protected]
Abstract In this paper we study the impact of polymorphism in Hindley/Milner style type systems that deal with some form of constraints. Previous approaches use a syntactic notion of constraints. This can have some serious limitations. Some reasonable programs are not typable under syntactic{based formulations of the quanti er introduction rule. As examples, we consider systems that deal with overloading and records. This problem can be solved if we switch to a semantic{ based treatment of constraints. Based on a novel semantic{ based formulation of the quanti er introduction rule, programs previously untypable become now typable. Furthermore, a semantic treatment of constraints allows to model a exible design space between an open and a closed world approach which can not be found in previous work.
1 Introduction The Hindley/Milner [Mil78] typing discipline introduces polymorphic types, so{called type schemes. This kind of parametric polymorphism has been used as the basic building block in the design of type systems for various programming languages. A number of extensions have been proposed to increase the expressiveness of the Hindley/Milner type system, such as record systems [Oho95, Rem89, Wan89], overloading [Kae92, VHJW96, NP93, CHO92, OWW95] and subtyping [AW93, EST95, Smi91]. A general theory of quali ed types has been studied by Jones [Jon92]. He used his system to study record and overloading systems. Based on these approaches, some even more advanced systems [Nis98, JJM97, Weh97] have recently been proposed. All these systems use a syntactic notion of constraints. This is re ected in the formulation of the quanti er introduction rule. We point out problems that can occur in these advanced systems that are due to their syntactic{based formulation of the quanti er introduction rule. As a tool we use the HM(X) framework to x these problems. The HM(X) framework uses a semantic notion of constraints. The basic idea of HM(X) is to consider the constraint system as a parameter of the type system. Speci c applications can be obtained by providing an appropriate constraint system X. For instance, the Hindley/Milner system is modeled by instantiating X to the standard Herbrand constraint system. The HM(X) framework can be seen as analogous to the CLP(X) framework in constraint logic programming [JM94]. In pre Supported
by DARPA Grant F30602-96-2-0232.
vious work [SOW97] we focused more on the algorithmic system. We could give a generic type inference algorithm for HM(X) type systems. Under sucient conditions on X type inference always computes principal types.
Contribution This work is concerned with type{theoretic
aspects of Hindley/Milner systems that deal with some form of constraints. Previous approaches use a syntactic notion of constraints. Such a treatment can have some servious limitations. We give some reasonable programs which are not typable under previous syntactic{based formulations of the quanti er introduction rule. This can be xed if we switch to a semantic notion of constraints. A semantic treatment of constraints allows to achieve a higher degree of polymorphism. More programs are now typable. Moreover, we present novel combinations of closed and open world approaches which can not be found in previous work. To achieve these improvements, we apply the HM(X) framework which comes with a semantic treatment of constraints.
Outline The rest of this paper is structured as follows. Section 2 gives an overview of the contribution of the paper. We give some motivating examples which reveal problems that can appear in the context of polymorphism and constraints. In Section 3 we give a general overview of the HM(X) framework. We present encodings of overloading [NP95, OWW95] and records [Oho95] in terms of HM(X) applications. These applications can be modeled fully and faithfully. That means, every program typable in the original approach is typable in the corresponding HM(X) application and vice versa. In Section 4 we give some resonable examples which are not typable under previous syntactic{ based formulations of the quanti er introduction rule. Such programs become typable if we switch to a semantic notion of constraints. Speci cally, we consider extensions of overloading and record system that deal with cyclic dependencies, curried overloading and message passing. A syntactic treatment of constraints in such systems has some clear disadvantages. Furthermore, we show how to model a
exible design space between an open and a closed world approach that can not be found in previous approaches. Section 5 establishes the connection to the theory of quali ed types, another general framework for type systems with constraints. In Section 6 we examine the common core of previous syntactic{based formulations of the quanti er introduction rule. Furthermore, we provide further arguments that underline the advantage of a semantic treatment of constraints. Section 7 concludes.
2 Overview
fx= let g y = (x:print) "Header Information"; (x:print) y in (g 1, g true) where we assume that the pairing operator ( ; ) and the sequencing operator ; are primitives contained in an initial type environment ?0 . Function g implements a more general print function that is based on the print functions in the IO{ library. Function g has to be of polymorphic type because we might want to apply function g to arguments with dierent types in the let-body. In the above example, we apply g to an integer and a boolean. But such a program is not typable in a calculus based on the (8 Intro{Ohori) rule. Consider an intermediate derivation. Typing judgments are now extended with a constraint component on the left hand side of the turnstile. ( :: ffprint : ! ()gg) ^ ( :: ffString ! ()gg) ^ ( :: U ); ?0 :x :
In order to give some motivation we consider Ohori's record calculus [Oho95]. We have record types of the form fl1 : 1 ; : : : ; ln : n g: Polymorphic type variables are kinded, restricting the possible instantiation types. Type schemes are of the form 8:( :: k) ) where type variable has kind k. A kind k is either the universal kind U denoting the set of all types or a record kind ffl : 0 gg denoting the set of0 all record types that contain a eld with label l and type . This allows to type various record operations in a concise way. For instance, the type of the record selection operator is written as follows: ( :l) : 8; :( :: U ) ^ ( :: ffl : gg) ) ! The function ( :l) takes as an argument any record that contains a eld with label l and extracts the eld value associated to l. Kinded record types have to ful ll some conditions. For instance, each eld label must have a unique type. In a later section, we show how to model such a record calculus by a constraint system. We now turn our attention to Ohori's formulation of the quanti er introduction rule which is written as follows: (8 Intro{Ohori)
`
y:(x:print) "Header Information"; (x:print) y : ! ()
We want to give function g a polymorphic type. That means, we have to quantify over type variable . In order to quantify over the side conditions of the (8 Intro{Ohori) rule have to be ful lled. But 2 fv(( :: ffprint : ! ()gg)), therefore we can not quantify over . Hence, Example 1 is not typable in a system based on the (8 Intro{Ohori) rule. The HM(X) framework [OSW98] oers the following quanti er introduction rule: ? ` e : 62 fv(C ) [ fv(?) (8 Intro{HM(X)) C ^ D; C ^ 9:D; ? ` e : 8:D ) In this formulation, type variables are not only quanti ed in the type scheme but also in the constraint. The projection operator 9 is one of the novel aspects of the HM(X) framework. By the projection operator 9 we can bind the variable in the constraint. This can also be seen as a kind of local binding. The projection operator is de ned by the constraint system. In the usual case where constraints are boolean algebras, projection corresponds to existential quanti cation. In HM(X) it is now possible to quantify over . Quanti cation over results in the constraint 1 9 :(( :: ffprint : ! ()gg) ^ ( :: U )) which is kept on the left hand side of the turnstile. In the let{body we apply function g to two arguments which results in the constraint C = ( :: ffprint : String ! ()gg)^ ( :: ffprint : Int ! ()gg)^ ( :: ffprint : Bool ! ()gg) because the constraint in the type scheme of function g has to be ful lled. In HM(X) we can type Example 1 in the following way: f : 8:C ) ! ((); ()) fx= let g : 8 :( :: ffprint : ! ()gg)^ ( :: U ) ) ! () g y = (x:print) "Header Information"; (x:print) y in (g 1, g true)
C ^ ( :: k); ? ` e : 62 fv(?) [ fv(C ) 62 fv(k) C; ? ` e : 8:( :: k) )
where the function fv computes the free variables. Note, other approaches use a similar formulation of the quanti er introduction rule. Hence, the following argumentation is not restricted to Ohori's approach. We consider an extension of Ohori's record calculus where we can see some limitations of Ohori's formulation of the quanti er introduction rule. Assume, we have a record calculus where we can overload eld labels. For instance, then we can deal with record types of the form fprint : Int ! (); print : Bool ! (); print : String ! ()g where () represents the unit type. Each eld represents a dierent implementation of the print function on integers, booleans and strings. Such a record calculus might be useful to group related functions into a record. Hence, a record type can be seen now as the speci cation for a library. Record selection corresponds to accessing a speci c function supplied in a given library. The above record type could represent the speci cation for a library that supplies IO{operations. Of course, there might be other useful applications of such a record calculus. We postpone a technical presentation of such a calculus to a later section. Consider now the following scenario. We could pass the IO{library to a function that has a local function that implements a more sophisticated print function, e.g. we rst print some header information and then the actual print out. Consider the following program.
Example 1
1 Note, ( :: f print : ! ()g ) ^ ( :: U ) corresponds to the constraint D in the above formulation of rule (8 Intro).
2
Note, the constraint 9 :(( :: ffprint : ! ()gg) ^ ( :: U )) disappears because the constraint (9 :(( :: ffprint : ! ()gg) ^ ( :: U ))) ^ C is semantically equivalent to the constraint C . We can conclude that the (8 Intro{Ohori) rule has some limitations if we extend Ohori's record calculus with some advanced features such as overloaded eld labels. There are programs where rule (8 Intro{Ohori) forbids quanti cation over a type variable. Such programs are now typable within the HM(X) framework. Another issue that arises in the context of polymorphism and constraints is the decision between an open and a closed world approach. This decision is usually re ected in the formulation of the quanti er introduction rule. First, we consider an open world approach. In an open world approach we assume that we do not have full knowledge about the whole world. There might be some facts about which we are not aware yet. To be more speci c, we consider Haskell style type classes [VHJW96, NP93, Jon92]. There we have constraints of the form C which denotes that belongs to the class C. Haskell's type class system follows an open world approach. Even empty type classes are considered legal. Consider the following typing judgment Eq ; ; ` x:y:eq x y : ! ! Bool where we assume that we have a type class declaration for the class Eq that de nes an equality function eq on type . We consider the type class approach in style of Nipkow/Prehofer [NP93]. Their quanti er introduction rule essentially reads as follows: D ^ C ; ? ` e : (8 Intro-Nipkow/Prehofer) 62 fv(?) [ fv(D) D; ? ` e : 8:C )
rule. There, we also keep the contraint over which we quantify in projected form on the left hand side of the turnstile. In the above example, we keep the constraint 9:Eq . Depending on how projection is de ned on constraints we can now model an open or a closed world approach. For instance, an open world approach can be modeled by a constraint rule of the form: 9:Eq =e true In such a system we nd typing judgment 1 again. A closed world can also be modeled easily. We simply do not specify the above mentioned constraint rule. As we will see later, the set of constraints allowed to appear in typing judgments and in type schemes is restricted to be a subset of all satis able constraints. The constraint 9:Eq is only satis able if there is an actual instance of class Eq . Assume we have an instance of class Eq de ned on integers. This is re ected in the constraint system by a rule of the form: true `e Eq Int The conditions put on constraint systems in HM(X) yield `e Eq Int `e 9:Eq : Then we nd typing judgment 1 again. We nd that there are some subtleties that arise in the context of polymorphism and constraints. Previous approaches uses a syntactic notion of constraints. Depending on the expressiveness of the type system, the degree of polymorphism that can be achieved in such systems can have some serious limitations. This relies on the syntactic{based formulation of the quanti er introduction rule in previous approaches. Such problems disappear if we switch to a semantic notion of constraints. Based on a semantic formulation of the quanti er introduction rule, programs previously untypable become typable. We apply the HM(X) framework which comes with a semantic treatment of constraints. Moreover, it is now possible to model an open and a closed world approach within one framework. Depending on the conditions we put on the constraint system we can model an open or a closed world approach.
Then application of the quanti er introduction rule results in the typing judgment true; ; ` x:y:eq x y : 8:Eq ) ! ! Bool (1) We do not know yet whether there is an instance declaration for the type class Eq. Therefore, the constraint Eq is simply traded from the left hand side of the turnstile into the constraint component of the type scheme. This is in contrast to a closed world approach. There we need full knowledge about the whole world at type checking time. In the above example this would require that we need to provide an actual instance of class Eq in order to trade the constraint Eq from the left hand side to the right hand side of the turnstile. Smith [Smi91] gave a formulation of a quanti er introduction rule that models a closed world approach: (8 Intro-Smith)
3 The HM(X) framework We now review the basic components of the HM(X) framework introduced in [SOW97]. The interested reader can nd a detailed development in [OSW98]. Types are members of an arbitrary term algebra T where there might be other constructors besides !. Type schemes include a constraint component C which restricts the types that can be substituted for the type variable . Types :: j ! Type schemes ::= j 8:C ) On the other hand, the language of terms is exactly as in [DM82]. That is, we assume that any language constructs that make use of type constraints are expressible as prede ned values, whose names and types are recorded in the initial type environment ?0 . Values v ::= x j x:e Expressions e ::= v j e e j let x = e in e Typing judgments are of the form C; ? ` e : where C is in X, ? is a type environment and is a type scheme.
C ^ D; ? ` e : 62 fv(?) C `e [=]D C; ? ` e : 8:D )
But it turns out that both approaches can be modeled within the HM(X) framework. Recall the (8 Intro{HM(X)) 3
(Var)
C; ?
(Abs) (App) (Let) (8 Intro) (8 Elim)
the set of solved forms to be the set consisting only of true, which is represented by the empty token set. Then the only type schemes arising in proof trees of valid typing judgments are of the form 8:fg ) , which we equate with Hindley/Milner type schemes 8:. It is easy to convince oneself that a judgment ? ` e : is derivable in Hindley/Milner if and only if fg; ? ` e : is derivable in HM(HERBRAND).
x : (x : 2 ?) C; ?x :x : ` e : 0 C; ?x ` x:e : ! 0
C; ?
`
C; ?x
`
e 1 : 1 ! 2 C; ? C; ? ` e1 e2 : 2
`
e:
C; ?x
`
`
e 2 : 1
3.2 Nipkow/Prehofer type classes
C; ?x :x : ` e0 : 0 let x = e in e0 : 0
C ^ D; ? ` e : C ^ 9:D; ? C; ?
`
`
We give now an HM(X) application that models type classes in style of Nipkow/Prehofer [NP95]. We refer to their approach as N P . We introduce constraints on type classes. The constraint ( :: fC1 ; : : : ; Cn g) denotes that type is in classes C1 ; : : : ; Cn . Quite often we also say that has kind2 S where S abbreviates C1 ; : : : ; Cn . For instance, (Int :: Eq) states that Int belongs to class Eq, the class of all equality types. This notation diers slightly compared to Haskell [VHJW96]. There, the constraint ( :: C) is written as C . It remains to de ne the rules of the constraint system TC that models type classes. The rules are as follows: TC1 ( :: C1 ) ^ : : : ^ ( :: Ce 2 ) `e ( :: fC1 ; : : : ; Cn g) TC2 ( :: fC1 ; : : : ; Cn g) ` ( :: Ci ) i 2 f1; : : : ; ng TC3 9:( :: S) =e true Rule TC3 states that we implement an open world approach. Even empty classes which do not have any instances are considered legal. This is along the lines as in [NP95]. The projection operator enables us to provide a semantic de nition of the open world approach. But these are not all constraint rules. We additionally have class and instance declarations in the term language that allow to modify the class hierarchy and provide implementations for class declarations. For each class or instance declaration we have to add some constraint rules. We have the following extended term language: Declarations p ::= e j inst T : (S)C where x = e in p j class : C S where x : in p where the term e stands for an expression. The class declaration class : C fC1 ; : : : ; Cn g where x : introduces the new class C as a subclass of all classes in fC1 ; : : : ; Cn g, which must have been already de ned. This corresponds to the Haskell declaration class (C1 ; : : : ; Cn ) ) C where x :: , where is the body of . An instance declaration inst T : (S)C where x = e expresses that T 1 : : : n is in class C provided that i are of kind Si . This corresponds to the Haskell declaration inst (con) ) C(T 1 : : : n ) where x = e where con is a list consisting of assumptions C'i where C' 2 Si for i 2 f1; : : : ; ng. For each new term construct we have a new typing rule. Each typing rule consists of two parts. A constraint part where new rules to the constraint system are added. The second part consists of usual typing rules. Consider the typing rule for class declarations:
62 fv(C ) [ fv(?) e : 8:D )
e : 8:D ) C `e [=]D C; ? ` e : [=]
Figure 1: Logical type system Furthermore, we restrict the set of constraints C that can appear in type schemes and on the left hand side of the turnstile to so{called solved forms. The set of solved forms, denoted by S , is always a subset of the satis able constraints in X. A constraint C ise satis able i `e 9fv(C ):C where the entailment relation ` between constraints is de ned by the constraint system X. However, depending on the actual constraint system used, we might put further restrictions on the set S . The typing rules can be found in Figure 1. Most rules are straightforward extensions of the standard Hindley/Milner rules. The formulation of the (8 Elim) rule is similar to previous formulations. The only valid instances of a type scheme 8:D ) are those that satisfy the constraint part of the type scheme. The term C `e [=]D states that the constraint C entails the constraint obtained by substituting type variable with type in D. Substitutions on constraints is connected to projection of constraints. In constraint system X we require that [=]D =e 9:(( = ) ^ D) where = is type equality and satis es at least the conditionseput on a congruence. It follows immediately that [=]D ` 9:D holds for any type . We have already pointed out the novel formulation of the (8 Intro) rule. We will discuss the relationship to previous formulations in later sections. In the following sections we give applications of the HM(X) framework that model the Hindley/Milner type system, type classes, overloading and records. We provide constraint systems that capture the properties of the application in mind. Furthermore, we relate the HM(X) application to the original approach on which the HM(X) application is based.
3.1 Hindley/Milner
( :: C)
The Hindley/Milner system is an instance of our type system framework. Take X to be the Herbrand constraint system HERBRAND over the algebra of types . HERBRAND0 consists only of primitive constraints of the form ( = ) where and 0 are elements of a term algebra T . Equality in HERBRAND is syntactic, T is a free algebra. Take
(Class) 2
4
`e
( :: C')
C' 2 S
C; ?:x : 8:( :: C) ) ` p : 0 C; ? ` class : C S where x : in p : 0
We omit curly brackets if the sort S contains only one element.
First, we add the new rule quantify over a constraint projection is always trivial. We conclude that the semantic{based formulation of the quanti( :: C) `e ( :: C') C' 2 S er introduction rule in HM(TC) corresponds exactly to the syntactic{based formulation in N P . That means, we have to the constraint system. After that, we infer the second a one{to{one correspondence between typing rules in N P part that consists of the judgment and HM(TC). We can summarize the results of this section in the following theorem. C; ?:x : 8:( :: C) ) ` p : 0 Theorem 1 (Full and Faithful) HM(TC) models N P fully Then we nd the conclusion and faithfully. C; ? ` class : C S where x : in p : 0 Hence, instead of Nipkow/Prehofer's system N P we can always use HM(TC). We can observe that the derivation ` is parameterized in the constraint system. While processing class declarations we update the constraint system and type check then the 3.3 Odersky/Wadler/Wehr overloading body of the class declaration with the updated constraint system. This observation reveals that actually the left hand We give now a translation of the overloading approach by side of the derivation ` consists of three parts: a constraint Odersky et al [OWW95] (to which we refer as O) in terms of set, a typing judgment and a set of constraint rules which an HM(X) application. Odersky et al. consider overloading describe the current constraint system. To make this explicit of functions where overloading is restricted to the argument we write C; ?; X ` e : where X represents the constraint type. For instance, it is possible to overloaded the print system used to infer this typing judgment. In this context function as described in the introduction. But it is forbidthe0 constraint system X is an extension of TC. We write den to overload numeric constants because the overloaded X = X [ R for extending constraint system X with a new identi er always has to be a function type and overloading set R of constraint rules. If the constraint system is xed we is restricted to the rst argument. simply write C; ? ` e : otherwise we make the constraint The overloading system O is modeled through a consystem explicit. straint system. Essentially, we have two dierent kinds of The typing rule for instance declarations looks a little constraints. Constraints of the form (o :: ! ) restrict the bit more complicated: possible instances of a type variable for a given overloaded identi er o. For instance, consider the following program: (1 :: C1 ) ^ : : : ( :: Cn ) f : 8; :(o :: ! ) ) ! `e fx=o x X' = X [ (T 1 : : : n :: C) The function f takes only arguments for which an instance with Ci 2 Si i 2 f1; : : : ; ng declaration of an overloaded identi er o with the same argument type is given. (Inst) 0 C; ?x ; X' ` p : The other kind of constraints are of the form (o :: T ). C 0 ^ C; ?x :x : 0 ; X ` e : [T 1 : : : n =] Constraints of this kind are used to describe instance declawith C 0 = (1 :: S1 ) ^ : : : ^ (n :: Sn ) and i new rations of overloaded identi ers. We use the subscript T on the type scheme T to describe the head type constructor of 0 = 8:( :: C) ) that type scheme. We put the following conditions on T : C; ?x ; X ` inst T : (S )C where x = e in p T = T ! fv( ) The last step in modeling Nipkow/Prehofer's type class j 8: ) T0 fv( ) fv(T0 ) approach consists of de ning the set S of solved forms. In TC the set S of solved forms consists of all satis able conwhere we assume that the constraint only contains predstraints of the form icates of the form (o :: ! ). Furthermore, we assume that T is a closed type scheme. C ::= fg j ( :: S) jC ^ C The constraints have to ful ll some rules. These rule are captured through the constraint system OVER de ned as We study now the connection between HM(TC) and the follows: original system N P . Essentially, both approaches dier only in the quanti er introduction rule. This rule reads in N P OVER1 (o :: ! ) ^ (o :: ! 0 ) `e ( = 0 ) as: (o :: T 1 ! 1 ) ^e (o :: T 2 ! 2 ) S); ? ` e : 62 fv(C ) [ fv(?) ` OVER2 (8 Intro{N P ) C ^ ( :: C; ? ` e : 8:( :: S) ) (1 = 2 ) ^ (1 = 2 ) This rule corresponds exactly to the (8 Intro) rule in HM(TC). OVER3 C ^ (oe:: 8:D ) ) `e (o :: [=] ) if C ` [=]D e C ^ D; ? ` e : 62 fv(C ) [ fv(?) OVER4 9 :(o :: ! ) = true (8 Intro) C ^ 9:D; ? ` e : 8:D ) Rules OVER1 and OVER2 enforce that the argument Remember, rule TC3 enforces that projection is always type uniquely determines the result type. Rule OVER4 trivial for constraints of the form ( :: S). All primitive conre ects the fact that we implement an open world approach. straints are of the form ( :: S). It follows that whenever we This is similar to type classes. 5
The set S of solved forms in HM(OVER) consists of all satis able constraints of the form C ::= fg j (o :: ! ) j C ^ C Furthermore, we put two additional conditions on the set S . First, we require that the constraints in S are in esimpli ed0 form. A constraint C is in simpli ed form if C ` ( = ) then `e ( = 0 ). For instance, (o :: ! ) ^ (o :: ! ! ) is not in simpli ed form because (o :: ! ) ^ (o :: ! ! ) `e ( = ! ) Finally, we impose an ordering relation on the type variables in a constraint in solved form. For every C 2 S there is an ordering < on the type variables in C such that for all predicates ( :: k) and 2 fv(k) with C `e ( :: k) we have that < . For instance, consider the constraint (o :: ! ). which is in solved form. It holds that < . This condition prohibits cyclic dependencies between type variables that appear in constraints. For each overloaded identi er o we add now primitives of the form o : 8; :(o :: ! ) ) ! to the initial typothesis ?0 . Note, the overloaded identi er o appears as a term variable but also in the constraint (o :: ! ). In this case, we additionally have instance declarations of the form inst o : T = e in p in the term language that overloads the meaning of the identi er o with the function given by e on all arguments that are constructed from the type constructor T . The extended term language is as follows:
Declarations
rule. The quanti er introduction rule in O reads essentially as: 62 fv(?) [ fv(C ) (8 Intro{O) C ^ ; ?C;`?e`: e : 8: ) where stands for the constraint (o1 :: ! 1 ) ^ : : : ^ (on :: ! n ). But this rule is an instance of the (8 Intro) rule in HM(OVER) where projection always has to be trivial. The side conditions in the (8 Intro{O) rule ensure that 9: =e true. It is also possible to give another equivalent formulation of the (8 Intro{O) rule. C ^ ; ? ` e : (8 Intro{O) there is no such that < C; ? ` e : 8: ) where the term < means with respect to the constraint C ^ . That means, if we require that projection always has to be trivial this imposes an ordering on the free type variables. Consider the following program where we assume that we have a function eq : 8: ! ! Bool in the initial type environment ?0 .
Example 2
f: 8 :8:(o :: ! ) ) ! Int fx= let g: ! Bool g = y. eq (o x) y in 1 It is not possible to give function g a polymorphic type. Projection would not be trivial, hence we rst have to quantify over type variable before we can quantify over . In HM(X) projection needs not necessarily to be trivial. We could now extend the set S of solved forms and additionally allow projection to appear in syntactic form. The set of solved forms consists now of all satis able and simpli ed constraints of the form C ::= fg j (o :: ! ) j C ^ C j 9:C which additionally obey the above ordering relation on the free type variables. Then we can type Example 2 in the following way. f: 8:9 :(o :: ! ) ) ! Int fx= let g: 8 :(o :: ! ) ) ! Bool g = y. eq (o x) y in 1 We nd that function g is now of polymorphic type. But function g can not be applied to arguments with dierent types. Every overloaded instance is uniquely determined by its argument type. In the above example this means that for every there can be only one such that we can provide an overloaded instance of the form (o :: ! ). This observation holds in general. The projection operator does not add any additional expressiveness to the overloading approach O. We only gave a constraint{based recast. We can state the following theorem. A proof sketch can be found in the appendix.
p ::= e j inst o : T = e in p
where the term e stands for an expression. We need one additional typing rule for creation of new instance declarations. In this case we also have to update the constraint system. This is similar to the type class example. X' = X [ (Inst)
`e
(o :: T )
C; ?; X ` e : T C; ?; X' ` p : 0 C; ?; X ` inst o : T = e in p : 0
We need one further side condition for the (Inst) rule. Assume there would be two instance declarations for an overloaded identi er o with the same head type constructor. Then we would nd two new constraint rules `e (o :: T0 ) and `e (o :: T ). The rules put on 0the constraint system ensure that3 the two type schemes T and T have to be equivalent . But this does not ensure that the meaning of the two instances are the same. Hence, we require that there is only one instance declaration for overloaded identi er o with head type constructor T . We study now the connection between HM(OVER) and the original approach O. Essentially, both approaches dier only in dierent formulations of the quanti er introduction 3 We refer to [OSW98] where we formally de ne equivalence between type schemes.
6
Theorem 2 (Full and Faithful) HM(OVER) (with or with- Section 3.4 we found that Ohori's record calculus can be enout projection appearing in syntactic form in S ) models fully and faithfully.
coded in terms of the overloading approach O. In Section 2 we pointed out problems that can arise in a record calculus with overloaded eld labels based on Ohori's formulation of the quanti er introduction rule. Odersky et al.'s and Ohori's formulation of the quanti er introduction rule are essentially the same. Hence, the argumentation found in Section 2 also applies to the extended overloading application. Wehr [Weh97] considered also extensions of the overloading approach O that introduce a form of curried overloading. His approach is also based on the formulation of the quanti er introduction rule in O. Hence, similar problems also occur in his approach. A short comparison between his approach and ours can be found in the appendix.
O
3.4 Ohori records
We consider now records in style of Ohori [Oho95]. In previous work [OSW98, Sul97] we already gave an HM(X) application that deals with records in style of Ohori. Furthermore, we discussed possible extensions of Ohori's record system. Here, we give an encoding of Ohori's record calculus in terms of the overloading application. This was observed by Odersky et al. [OWW95]. The basic idea is to consider a record constraint of the form ( :: ffl : gg) as an overloaded constraint of the form (l :: ! ) where the eld label l becomes now an overloaded identi er. Record creation corresponds to providing instance declarations and record selection corresponds to applying an overloaded identi er (which corresponds to a eld label) to a record. For more details, we refer to [OWW95]. The following theorem establishes the connection between the overloading approach HM(OVER) and Ohori's record calculus. Theorem 3 (Full and Faithful) Given the HM(OVER) application. Then we can give an encoding of Ohori's record calculus in HM(OVER) that is full and faithful.
4.2 Cyclic dependencies
In HM(OVER) we imposed an ordering relation on type variables that occur in constraints in solved form. This condition is crucial to establish the full{ and faithfulness connection between O and HM(OVER). If we would give up this condition in the overloading application O, the syntactic{ based treatment of constraints in O would have some clear disadvantages. Consider the following program typed in HM(OVER) where we do not require an ordering relation on type variables occuring in constraints in solved form.
Example 3
4 Polymorphism and constraints
f : 8:(9 :((o :: ! ) ^ (o0 :: ! ))) ) 8 :(o :: ! ) ^ (o0 :: ! ) ) ! ! (Bool; Bool) fxy= (eq (o0 y) x, eq (o x) y)
In this section we point out problems that occur due to a syntactic treatment of constraints. We come back to the problems mentioned in the Overview Section 2. We study these issues in more depth, respectively give more examples where such problems might occur. To this purpose we consider extensions of the overloading approach O introduced in Section 3.3. We consider extensions that are along the lines of [JJM97, Nis98, Weh97]. The rst three sections give further examples where one can see that previous formulations of the quanti er introduction rule have some shortcomings. In the last section, we show how to model a exible design space between an open and a closed world approach.
In O we could not give function f a polymorphic type. Due the syntactic{based formulation of the quanti er introduction rule in O, we neither could quantify over , nor over . But obviously, function f can be applied to arguments with dierent types. This example is weaker compared to the others. We could slightly update the (8 Intro{O) rule and allow to quantify over a set of type variables.
4.1 Curried overloading
(8 Intro{O)
We extend the overloading approach O with curried overloaded functions. For a technical presentation of such a calculus with curried overloading we refer to the appendix. In such an extension we nd constraints of the form (o :: ! ! ). The idea is now that we not only can overload the rst argument but also the second argument. Remember, we never allow to overload the result type. Such a calculus proves to be useful in dealing with printer functions where we not only can specify the format but additionally also the device. For instance, print : HP{Printer ! Postscript ! () speci es a print function that supports the Postscript{format for a speci c printer. We could also think of print function that supports a speci c le format for a hard disk, speci ed by print : Harddisk ! Unix ! (). Furthermore, it follows immediately that we can encode the record calculus with overloaded eld labels based on such an extended overloading application. Remember, in
C ^ 1 ^ : : : ^ n ; ? ` e : there is no such that < 1 ; : : : ; < n C; ? ` e : 8 :1 ^ : : : ^ n )
Then we could give Example 3 a polymorphic type in O under the above generalized quanti er introduction rule. f : 8: :(o :: ! ) ^ (o0 :: ! ) ) ! ! (Bool; Bool) fxy= (eq (o0 y) x, eq (o x) y) Nevertheless, the semantic{based quanti er introduction rule in HM(X) can be seen as the proper rule to deal with cyclic dependencies.
7
4.3 Message passing
4.4 Non{simple contexts
Recently, there have been a proposal by Nishimura [Nis98] for a calculus where method names can be passed around as arguments. Method names are treated as eld labels in a record. In previous work [Sul97] we introduced an experimental record calculus where eld labels are considered as rst class values. We have already observed that we can encode Ohori's record calculus based on the overloading approach by Odersky et al. We simply extend Odersky et al.'s overloading approach with rst class overloading identi ers. That means, overloaded identi er can now be passed around as arguments. We refer to the appendix for the technical presentation of such a calculus. Nishimura's record calculus is based on Ohori's record calculus. More speci cally, Nishimura's formulation of the quanti er introduction rule is equivalent to Ohori's formulation. Hence, we can encode his calculus in terms of the extended overloading application where overloaded identi ers are rst class values. The encoding is done along the lines as for Ohori's calculus. We point out now some problems in Nishimura's calculus that are similar to the problem discussed in the introduction. Essentially, we give a recast of Example 1 in terms of a record calculus with rst class eld labels. The following program is typable in HM(X) but not in a calculus based on the (8 Intro{Ohori) rule. The reason is the same as in Example 1. Projection always has to be trivial in the (8 Intro{Ohori) rule which does not hold in the following example. Hence, this example would not be typable in Nishimura's calculus.
In the overloading apporoach O we always require that constraints which appear in type schemes are simple. Simple means that constraints in solved form are of the form (o :: ! ). The argument type always has to be a type variable. In the context of Haskell's type classes this restriction is known as the simple context restriction. There is a recent proposal [JJM97] for type classes that relaxes this restriction. Wehr [Weh97] gave a similar proposal in the context of the overloading approach O. In the appendix we give the technical presentation of an extended HM(X) overloading application that deals additionally with overloaded identi ers with non{simple argument types. Note, this application does not incorporate all features proposed in [Weh97]. We just introduce the kind of non{simple contexts that are necessary to illustrate the shortcomings that might arise in such a setting. Assume we want to write a function that uses an overloaded identi er that operates on a list of arbitrary types. The term e stands for the following program where the primitive head extracts the rst element of a list.
Example 5
g : [] ! g = y. head (o y) We obtain the following typing judgment (o :: [] ! []); ?0 ` e : [] ! O models an open world approach. Projection is only trivial for overloaded identi ers with simple argument type, see constraint rule OVER4 in Section 3.3. It is reasonable to require that if we constrain the arguments of an overloaded identi er further, we have to give an instance declaration for that speci c overloaded identi er. This corresponds to a closed world approach. In the introduction we already observed that HM(X) is able to model an open and a closed world approach. This can be achieved by the way how projection is de ned on constraints. Even better, we can mix an open and a closed world approach within one application. We want to model an open world approach for overloaded identi ers with simple contexts. For overloaded identi ers with non{simple contexts we want to model a closed world approach. This can be achieved by the conditions we put on the constraint system. Projection is always trivial for simple overloaded identi ers but not for non{simple overloaded identi ers as for example (o :: [] ! []). There is no equivalent to rule OVER4 for non{simple overloaded identi ers. Consult the appendix for a description of the constraint system that models an overloading application with non{simple contexts. Consider now again the above typing judgment. We want to quantify over type variable . In HM(X) we keep the projected constraint 9:(o :: [] ! []) on the left hand side. If there are no instance declarations of the form o : [] ! [] the constraint 9:(o :: [] ! []) is unsatis able, hence the program is not even typable. Remember, constraints that occur in typing judgments have to be at least satis able. Assume we have instance declarations of the form o : [Int ]! [Int] and o : [Bool] ! [ Bool ]. Hence, we know that `e (o :: [Int] ! [Int]) and `e (o :: [Bool] ! [Bool]) holds. But then we nd that 9:(o :: [] ! []) is semantically equivalent to true. We obtain the following judgment.
Example 4 f : 8:( :: ffprint int : Int ! ()gg)^ ( :: ffprint bool : Bool ! ()gg) ) ! ((); ()) fx= let g: 8print; :( :: ffprint : ! ()gg) ) print ! ! () g = print, y . (x.print) y in (g print int 1, g print bool true)
The function g takes as an additional parameter now an overloaded identi er. Note, we distinguish between the term variable print and the type variable print (The same holds for print int and print bool.). The term print has type print. The type variable print ranges only over overloaded identi ers (Actually, print ranges only over eld labels but we assume that the record calculus with rst class eld labels is encoded in terms of an overloading application with rst class overloaded identi ers.). The constraint ( :: ff : gg) takes now three arguments. We use sorts to ensure that the second argument always has to be an overloaded identi er. This is re ected in the term algebra which is now multi{ sorted. Actually, Nishimura's calculus is a little bit more restrictive. A method name (which corresponds to a eld label resp. an overloaded identi er) needs always to be attached to its arguments. We could put stronger conditions on the extended overloading application to enforce this. In the above example a method name is always passed with its argument, hence this example still applies to Nishimura's calculus. 8
true; ?0 ` e : 8:(o :: [] ! []) ) [] ! We can observe that the HM(X) framework provides us with a exible design space in modeling an open or a closed world approach. An open world approach for overloaded identi ers with simple argument types is modeled by requiring projection to be trivial for such constraints. There is no such rule for overloaded identi ers with non{simple arguments. Therefore, we model a closed world approach for such overloaded identi ers.
5 Quali ed types The theory of quali ed types by Jones was also introduced to study type systems that support some form of constraints. The quanti er introduction rule in the theory of quali ed types by Jones [Jon92] does not impose an ordering on the free type variables: 62 fv(?) [ fv(C ) (8 Intro-Jones) C ^ D; ?C;` ?e `: e : 8:D ) Hence, the counter examples would not apply to the theory of quali ed types. But Jones' quanti er introduction rule is not able to deal with a closed world approach. Recall Example 5 in Section 4.4 which would be typed as follows in Jones framework: true; ?0 ` e : 8:(o :: [] ! []) ) [] ! This scheme runs into problems if there is no instance declaration of the form (o :: [] ! []). Because then the constraint (o :: [] ! []) is unsatis able and we can not apply the function to any argument. Jones overcomes this problem by treating constraints as proof obligations. Whenever we want to apply a polymorphic function to an argument we have to provide evidence that we nd an appropriate instance. Jones gives a semantic based on the idea of dictionaries. Every function takes a dictionary that corresponds to an actual instance. The problem still remains if we de ne a local function with unsatis able constraint component but never apply this function in the let{body. In the following example we assume that function g does not appear in the body of the let{statement.
Example 6
let g = y. head (o y) in : : : Furthermore, we assume that there is no satisfying instance for the constraint (o :: [] ! []). Then this program would be type{correct in the theory of quali ed types but rejected in HM(X).
6 Discussion In the previous sections we discussed several approaches [NP95, OWW95, Oho95] and presented corresponding HM(X) applications. The quanti er introduction rules in the approaches by Odersky et al. and Ohori are special versions of the quanti er introduction rule in the theory of quali ed types [Jon92]. 9
These approaches introduce special forms of constraints to deal with overloading or records. We gave an equivalent formulation for their quanti er introduction rules. This formulation is written as follows: C ^ D; ? ` e : 62 fv(?) (8 Intro{3) there is no such that < C; ? ` e : 8:D ) where an ordering relation is imposed on the free type variables. This syntactic formulation of the (8 Intro) rule corresponds to the (8 Intro) in HM(X) where projection always has to be trivial. In case of the type class approach by Nipkow/Prehofer there is no ordering relation imposed on the free type variables because a type class constraint ( :: S) always depends only on one variable. Furthermore, the projection operator gave a semantic justi cation of the open world approaches by Odersky et al. and Nipkow/Prehofer. We found that the corresponding HM(X) applications model the original approaches [NP95, OWW95, Oho95] fully and faithfully. The syntactically formulated (8 Intro{3) rule has some serious limitations if we extend the expressiveness of the type system. We pointed out limitations that can arise in extensions of the approaches by Odersky et al [OWW95] and Ohori [Oho95] which are based on rule (8 Intro{3). The central problem is that rule (8 Intro-3) imposes an ordering relation on the free type variables to ensure that projection is always trivial. We gave some reasonable examples which are not typable under rule (8 Intro{3). The novel semantic formulation of the quanti er introduction rule in HM(X) releases us to impose an ordering relation on the free type variables. This formulation relies on the projection operator that enables us to keep track of quanti ed constraints. Quanti ed constraints are kept in projected form on the left hand side of the turnstile. The critical examples we gave are now typable under the HM(X) (8 Intro) rule. But we can do even more. Depending how projection is de ned on constraints, we can model an open and closed world approach within one framework. An open world approach is modeled in the overloading application in Section 3.3. An example of a closed world approach can be found in Section 4.4. There we could even see that we can mix both approaches. Surprisingly, most times the projection operator does not appear in syntactic form. While quantifying over a type variable , projection is either trivial, therefore for the traded constraint De it holds that 9:D =e true, or there was a such that C ` [=]D where C is the constraint kept on the left hand side of the turnstile. The rst case corresponds to the open world approach and the second case to the closed world approach. In both cases projection is always resolved while we type check a program. But it might be desirable to type check a program without knowing all instance declarations in advance. Such a situation might occur in a module system. At compile time (that is when we perform type checking) we do not require that all instance declarations (which correspond to the implementation part) are visible. We only require that the definition part is visible. In case of the overloading approach O that would mean we only have to provide the primitive function for each overloaded identi er o. Then at link time we match the speci cation inferred at compile time with a given set of instance declarations. Right now, we always match with the true constraint. We would like to express within a constraint what kind of instance declarations have to be
in the program. Remember, term constructs that make use of constraints are expressed as primitives. Whenever we use a primitive we could set a link to the context in which this primitive is used. The link would be stored in the constraint of the primitive. But this is more an issue of detecting type errors while performing type inference. We left out the issue of type inference in this paper. The detection of type errors is an interesting topic in itself. We leave it to future research to investigate this topic further.
provided in order to run a program. For instance, in case of Example 5 in Section 4.4 we would like to obtain 9:(o :: [] ! []); ?0 `
e : 8:(o :: [] ! []) ) [] !
The constraint 9:(o :: [] ! []) can be seen as the constraint that has to be ful lled by a given library of instance declarations in order to run the program. Up to now we always have to provide an actual instance, therefore projection does not appear anymore in syntactic form. The reason is that constraints in solved form always have to be satis able. The idea is that we simply give up satis ability. This is possible because the typing rules preserve satis ability. The constraints in the premise of a typing judgment are satis able i the constraints in the conclusion of a typing judgment are satis able. This observation holds because we know that C is satis able i 9:C is satis able which itself is an easy observation. That means while we type check a program we do not require satis ability of solved forms anymore. Checking satis ability of constraints is postponed to link time. We nd that projection appears now in syntactic form, see the previous typing judgment. There are a couple of implications if we give up satis ability. For instance, in HM(OVER) we usually require that all ground instances have to be resolved, e.g. the constraint (print :: Int ! ()) is true or not. This is also re ected in the set of solved form where only non{ground constraints of the form (o :: ! ) are allowed. But now a constraint in solved form need not to be satis able anymore. Furthermore, we assume that at compile time we might not have access to all instance declarations. Therefore, it might be reasonable to admit additionally ground constraints as constraints in solved form. But then the constraints might become more verbose, e.g. consider the following program that uses some standard print functions. (print :: Printer ! Dvi ! ())^ ; ?0 (print :: Printer ! Text) ! ())
7 Conclusion In this paper we pointed out problems that might occur in the context of polymorphism and constraint. Such problems arise due to a syntactic treatment of constraints found in previous work. We gave some reasonable programs that are not typable under previous syntactic formulations of the quanti er introduction rule, see Sections 2, 4.1 and 4.3. Furthermore, in Sections 2,4.4 and 6 we studied issues that arise in the context of open vs closed world approach. Based on a semantic treatment of constraints, we can substantually improve previous approaches. As a tool we used the HM(X) framework that comes with a semantic notion of constraints. As a side product, we gave constraint{based formulations of several overloading approaches [NP95, OWW95] in terms of HM(X) applications. We incorporated additional features into the overloading approach by Odersky/Wadler/Wehr [OWW95] that are along the lines of the extensions proposed in [JJM97, Nis98, Weh97]. Ohori's record calculus, respectively extensions of his calculus, fall out by encodings in terms of the overloading application. These extended applications are interesting in its own right. We did not consider type inference for these applications. This is an important issue for the practical relevance of the described extensions. We conjecture that for all presented applications type inference is decidable and a principal types theorem hold. We are working on formal proofs for this conjecture. We want to give some intuition why we believe that type inference is feasible for the discussed applications. In [JJM97, Weh97] it is conjectured that extensions similar to ours retain decidable type inference with principal types. Usually, type inference involves solving of constraints. HM(X) diers from previous approaches that the projection operator might appear in syntactic form in such constraints. But it turns out that it is sucient to consider only solving of constraints that are projection{free 4 . Arbitrary constraints can be solved by solving the projection{free part rst and then lifting the result to the projected constraint part. A discussion can be found in the appendix. The overall conclusion we draw is that there are some subtleties that arise in the context of polymorphism and constraints. The syntactic treatment of constraints found in previous work can have some serious limitations. We showed that a semantic treatment of constraints has some clear advantages. In this paper, we focused on the type{ theoretic implications of the use of a semantic treatment of constraints. We left out constraint{theoretic aspects as e.g. decidability of entailment, complexity etc. We postpone the investigations of such issues to future work.
`
(print standard-printer dvi{ le; : ((); ()) print standard-printer text) We might assume that these two print functions are supplied by a standard library. Hence, such constraints should always be resolved and therefore should not appear in syntactic form in constraints in solved form. But this is more an issue how we de ne the set of solved forms for a speci c implementation. Up to now we did not put many conditions on the set of solved forms. It depends on the speci c implementation and on the context in which the application is used what kind of conditions we put on solved forms. Another advantage of giving up satis ability at compile time is that we can now match with dierent implementations, e.g. an instance declaration might be implemented in dierent ways in dierent libraries. A disadvantage might be that we now postpone type errors. For instance, assume a program needs to ful ll the instance declaration print : Harddisk ! MSDOS) ! (). But we know that the harddisk never supports the MSDOS le format. Hence, the type error occurs at link time and not at compile time. But this is more a question of how to detect type errors in general. One solution might be that every constraint contains a link to the program where this constraint occurred. We could even point to the exact position
4 A constraint is projection{free if the projection operator does not appear in syntactic form in that constraint.
10
Acknowledgements
[NP95]
I am grateful to Paul Hudak, Patrick Jansson, Alastair Reid, Valery Trifonov and Martin Wehr for helpful discussions and remarks. Furthermore, I would like to thank the attendants of the SPAM meeting group for their patient and feedback they gave me.
[Oho95] [OSW98]
References [AW93]
Alexander Aiken and Edward L. Wimmers. Type inclusion constraints and type inference. In FPCA '93: Conference on Functional Programming Languages and Computer Architecture, Copenhagen, Denmark, pages 31{41, New York, June 1993. ACM Press. [CHO92] Kung Chen, Paul Hudak, and Martin Odersky. Parametric type classes. In Proc. of Lisp and F.P., pages 170{191. ACM Press, June 1992. [DM82] Luis Damas and Robin Milner. Principal type schemes for functional programs. January 1982. [EST95] Jonathan Eifrig, Scott Smith, and Valery Trifonov. Type inference for recursivly constrained types and its application to object oriented programming. In Electronic Notes in Theoretical Computer Science, volume 1, 1995. [JJM97] Simon Peyton Jones, Mark Jones, and Erik Meijer. Type classes: an exploration of the design space. In Haskell Workshop, June 1997. [JM94] Joxan Jaar and Michael Maher. Constraint logic programming: A survey. Journal of Logic Programming, 19(20):503{581, 1994. [Jon92] Mark P. Jones. Quali ed Types: Theory and Practice. D.phil. thesis, Oxford University, September 1992. [Kae92] Stefan Kaes. Type inference in the presence of overloading, subtyping and recursive types. In 1992 ACM Conferenc on Lisp and Functional Programming, pages 193{204. ACM, ACM, August 1992. [Mil78] Robin Milner. A theory of type polymorphism in programming. Journal of Computer and System Sciences, 17:348{375, Dec 1978. [Nis98] Susumu Nishimura. Static Typing for Dynamic Messages. In Conference Record of POPL '98: 25th Annual ACM SIGPLAN{SIGACT Symposium on Principles of Programming Languages, San Diego, Calif. ACM, January 1998. [NP93] Tobias Nipkow and Christian Prehofer. Type checking type classes. In Conference Record of the Twentieth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, Charleston, South Carolina, January 10{13, 1993, pages 409{418. ACM Press, January 1993.
[OWW95]
[Rem89] [Smi91] [SOW97]
[Sul97] [VHJW96] [Wan89] [Weh97]
Tobias Nipkow and Christian Prehofer. Type reconstruction for type classes. Journal of Functional Programming, 5(2):201{224, 1995. Atsushi Ohori. A polymorphic record calculus and its compilation. ACM TOPLAS, 6(6):805{ 843, November 1995. Martin Odersky, Martin Sulzmann, and Martin Wehr. Type inference with constrained types. Theory and Practice of Object Systems, 1998. to appear. Martin Odersky, Philip Wadler, and Martin Wehr. A second look at overloading. In Proc. ACM Conf. on Functional Programming and Computer Architecture, pages 135{1469, June 1995. D. Remy. Typechecking records and variants in a natural extension of ML. pages 77{88. ACM, January 1989. Georey S. Smith. Polymorphic type inference for languages with overloading and subtyping. PhD thesis, Cornell University, Ithaca, NY, August 1991. Martin Sulzmann, Martin Odersky, and Martin Wehr. Type inference with constrained types. In FOOL4: 4th. Int. Workshop on Foundations of Object-oriented programming Languages, January 1997. Martin Sulzmann. Designing Record Systems. Research Report YALEU/DCS/RR-1128, Yale University, Department of Computer Science, April 1997. Cordelia V.Hall, Kevin Hammond, Simon L. Peyton Jones, and Philip L. Wadler. Type classes in Haskell. ACM TOPLAS, 18(2):109{ 138, March 1996. Mitchell Wand. Type inference for record concatenation and multiple inheritance. In Proceedings of the IEEE Symposium on Logic in Computer Science, pages 92{97, June 1989. Martin Wehr. Uberladung in Typsystemen. PhD thesis, Universitat Karlsruhe, October 1997.
A Full and Faithful We give a proof sketch for the overloading application O. The other cases are similar. Theorem 4 (Full and Faithful) HM(OVER) (with or without projection appearing in syntactic form in S ) models O fully and faithfully. Proof: We have to show that given an expression e and a type environment ?. Then e is typable under ? in O i e is typable under ? in HM(OVER). It is an easy observation that HM(OVER) models O faithful. The (8 Intro{OVER) rule in HM(OVER) subsumes the (8 Intro{O) rule in O. Hence, every judgment in O is also derivable in HM(OVER). 11
B Overloading with non{simple contexts
It remains to proof the fullness direction. Given a judgment C; ? ` e : in HM(OVER), we have to show that e is typable under ? in O. We apply induction over the derivation tree of the judgment C; ? ` e : . We consider the number of (8 Intro{OVER) rules with non{trivial projection. Recall, in Section 3.3 we have already observed that the (8 Intro{OVER) rule is strictly more powerful than the (8 Intro{O) rule. There are cases where rule (8 Intro{O) forbids to quantify over a free type variable. Such cases occur if projection is non{trivial. But this is possible under rule (8 Intro{OVER), see Example 2. The proof proceeds by rewriting the derivation tree and omitting application of the (8 Intro{OVER) rule with non{trivial projection. It turns out that application of the (8 Intro{OVER) with non{trivial projection does not allow to type more programs. We consider the last occurence of a (8 Intro{OVER) rule with non{trivial projection in the derivation tree. Note, we assume that the sub{term in the sub{judgment is used in a monomorphic context. That means, the polymorphic type of the sub{term will be instantiated to a monomorphic type in a later part of the derivation tree. Otherwise, we could immediately omit application of the (8 Intro{OVER) rule. We examine the last (8 Intro{OVER) rule with non{ trivial projection in the derivation tree of C; ? ` e : . We nd the following situation: C 0 ^ D; ?0
We only have to make slight changes to deal with non{simple contexts. Compared to the original approach, conditions OVER1 and OVER2 are collapsed into one rule. OVER1 (o :: ! 1 ) ^ (o :: !e 10 ) `e (1 = 10 ) OVER2 C ^ (oe:: 8:D ) ) ` (o :: [=] ) if C ` [=]D OVER3 9:(o :: ! ) =e true The set S of solved forms consists of all satis able and simpli ed constraints of the form C ::= fg j (o :: ! ) j (o :: T ! ) j C ^ C We additionally admit now constraints of the form (o :: T ! ) in solved form because we allow non{simple contexts. The rest remains the same. Note, if we would allow arbitrary contexts like (o :: T T ! ) we would end up in an undecidable type system where type inference is not feasible anymore. A discussion about this issue and under what kind of conditions the type system is still decidable and type inference might be feasible can be found in Wehr's PhD{thesis [Weh97]. An HM(X) application that models his advanced overloading system should be possible along the lines of the previous development.
e0 : 0 62 fv(?0 ) [ fv(C 0 ) there is a such that < C 0 ^ 9 :D; ?0 ` e0 : 8 :D ) 0 We consider two cases. If 62 fv(?0 ) we could switch the order of quanti cation and rst quantify over . Hence, we assume that we deal with a situation where we can not reorder the order of quanti cation. This enforces that 2 fv(?0 ). `
C Overloaded curried functions An overloaded identi er might now depend on two dierent type constructors. T1 T2 = T1 ! T2 ! fv( ) [ j 8; : ) T0 1 T2 fv( ) fv(T0 1 T2 ) where we assume that the constraint only contains predicates of the form (o :: ! ! ). Furthermore, we assume that T1 T2 is a closed type scheme. The other constraints are of the form (o :: ! ! ). Compared to the original overloading approach condition OVER2 is split into two conditions to deal with curried overloaded functions. (o :: ! ! ) ^e(o :: ! ! 0)
Note, this hold only because we have imposed an ordering relation on the type variables in a constraint. Then, we can consider as an arbitrary but xed type variable. This observation is important in a later part of the proof. By assumption the polymorphic type 8 :D ) 0 is used in a monomorphic context. We consider the possible instantiations for the type variable . Assume, 1 = [1 = ] and 2 = [2 = ] are possible instantiations for the type variable . Furthermore, we know that there must be a constraint of the form (o :: ! ) in D with 2 fv( ). Note, in general there is a sequence of constraints of the form (o1 :: 1 ! 1 ); : : : ; (on :: n ! n ) with i 2 fv(i?1 ) and 2 fv(n ). W.l.o.g., we only consider the simplest possible case. Remember, for a xed (we know that can not be a polymorphic type variable) the result type in the constraint (o :: ! ) is uniquely determined. This is enforced by the rules put on the constraint system OVER. Hence, we nd that 1 = 2 . Therefore, we can omit application of the (8 Intro{OVER) rule. Because the polymorphic type variable can not be instantiated to dierent types. Instead we can use the sub{judgment C 0 ^ 9 :D; ?0 ` e0 : 1 . This yields a derivation tree of judgment C; ? ` e : where the number (8 Intro{OVER) rules with non{trivial projection is reduced by one. Application of the induction hypothesis yields that the term e is typable under ? in O. Speci cally, there is a derivation tree of the judgment C; ? ` e : where all applications of the quanti er introduction rule are instances of rule (8 Intro{O). This concludes the proof.
OVER1
`
( = 0)
OVER2
(o :: T 1 ! S 10 ! 1 ) ^e (o :: T 2 ! S 20 ! 2 )
OVER3
(o :: T 1 ! ! 1 ) ^e (o :: T 2 ! ! 2 )
OVER4
(o :: ! T 1 ! 1 ) ^e (o :: ! T 2 ! 2 )
`
(1 = 2 ) ^ (10 = 20 ) ^ (1 = 2 ) `
(1 = 2 ) ^ (1 = 2 ) `
(1 = 2 ) ^ (1 = 2 )
OVER5 C ^ (oe:: 8:D ) ) `e (o :: [=] ) if C ` [=]D OVER6 9:(o :: ! ! ) =e true Note, condition OVER2 states that as soon as the head
type constructors of the two argument types are xed the 12
result type is uniquely determined but not earlier. Conditions OVER3 and OVER4 state that if one argument type is xed and the head type constructor of the other argument type is known then the result type is uniquely determined. Condition OVER1 covers the case if the two argument types are type variables. The set S of solved forms consists of all satis able and simpli ed constraints of the form C ::=
OVER1 (o :: ! ) ^ (o :: ! 0 ) `e ( = 0 ) OVER2
(o :: T 1 ! 1 ) ^e (o :: T 2 ! 2 ) `
(1 = 2 ) ^ (1 = 2 )
OVER3 C ^ (oe:: 8:D ) ) `e (o :: [=] ) if C ` [=]D OVER4 9:9o:(o :: ! ) =e true Note, condition OVER4 diers now slightly because we
fg j (o :: ! ! ) j C ^ C
Wehr [Weh97] also introduces curried overloaded functions. The idea is to encode curried overloaded functions in terms of the basic overloading system. For instance, consider two overloaded instances o : 8 :C ) T ! S ! 1 0 ) T ! T ! 2 o : 8 :C where we have two constant type constructors T and S . Instead of these two instances he introduces o : 8; :(o0 :: ! ) ) T ! ! where for each argument type we have instance declarations o0 : 8 :C ) S ! 1 0 ) T ! 2 o0 : 8 :C
deal with overloaded variables. If the overloaded identi er is yet unspeci ed we do not want projection to be trivial. The de nition of solved forms is standard. The set S of solved forms consists of all satis able and simpli ed constraints of the form C ::= fg j (o :: ! ) j C ^ C We also do not need anymore primitives for each overloaded constants. We have now the following primitive in the initial type environment ?0 : @ : 8o; ; :(o :: ! ) ) over(o) ! ! This primitive can be read as invoke overloaded identi er o at argument . Instead of o x we write now @ o x. Furthermore, we require that instance declarations are only allowed for overloaded constants. Note, in order to model the Example 4 we actually need an overloading application that models rst class overloaded identi ers combined with curried overloading. For instance, the record constraint ( :: ffprint : ! ()gg) in function g translates to the overloaded constraint (print :: ! ! ()) where print is an overloaded type variable and we additionally overload the second argument type. It is straightforward to combine the curried and rst class overloading approach into an overloading approach that combines these two features. In order to keep the presentation simple we omit to give the presentation of such a calculus.
Further details can be found in [Weh97]. In Wehr's curried overloaded function approach it is not obvious how to encode records with overloaded eld labels. For instance, in his system it is not possible to type the program 5 in Example 1. We postpone a detailed discussion of the commonalities and dierences between the two approaches to future work.
D Overloaded identi ers as rst class values In the previous applications, overloaded identi ers were considered as constants. Now, overloaded identi ers become rst class values that can be passed around as arguments. This requires a couple of changes. In our term language we distinguish now between overloaded constants oc and overloaded variables ov . This is in contrast to the previous applications were overloaded identi ers were considered as primitives. This change requires that we have to distinguish between overloaded constants and variables also in the type language. Over o ::= oc j ov Types ::= j over(o) j ! An overloaded type consists now either of a constant oc or a variable ov . The constraint ( :: ) takes now as an additional parameter an overloaded identi er type. We assume that the signature of the predicate ( :: ) speci es that the rst argument always has to be an overloaded identi er. The constraint rules are almost the same as in the original application. In the following rules the term o stands for an overloaded type variable.
E Type Inference We present some initial results that let us conjecture type inference with principal types is possible for all discussed HM(X) applications. One of the novelties of the HM(X) framework is the projection operator. We have to keep this in mind when we perform type inference. Type inference involves solving of constraints. We also refer to solving of constraints as constraint normalization. The constraint normalization function normalize takes a constraint problem (D; ) and returns its principal normal form or reports failure. We refer to [OSW98] for a general discussion about type inference in HM(X). We make no attempt to review the terminology about type inference in HM(X), see [OSW98] for details. The projection operator can appear in syntactic form in constraint problems. That means, normalization has to deal with projected constraints. This is in contrast to previous approaches. There, normalization only considers projection{ free constraints. A constraint C is projection{free if C considered as set consists only of tokens of the form (o :: ) and ( = 0). It turns out that it is sucient to consider
5 Remember, this example is based on the curried overloading application.
13
Lemma 3 Let D be a projection{free constraint in X and
only solving of constraints that are projection{free, i.e. the projection operator does not appear in syntactic form. We can then lift such a solved projection{free constraint to its projected counterpart. In the following we provide a method how to normalize arbitrary constraints (projection may appear in syntactic form) giving a procedure that normalizes projection free constraints. This let us conjecture that type inference with principal types is possible for the discussed HM(X) applications. In [OWW95, Oho95, NP95] it is proven that the approaches N P , O and Ohori's record calculus satisfy the principal type property. Hence, we nd that the corresponding HM(X) applications (where projection might appear in syntactic form) satisfy the principal type property, too. It is still open whether the extended overloading applications satisfy the principal type property. But initial results [Weh97, JJM97, Nis98] show that it should be possible to prove a principal type property. Then we can use these results to obtain type inference with principal types for the corresponding HM(X) applications. We give now a proof how to lift a normalization procedure for projection{free constraints to the general case. The rst observation we make is that we can always split a constraint into its projection free subpart. The idea is that we can always rename type variables which are bound by the projection operator. It holds that 9:C =e 9 :[ =]C where is a new type variable. That means, w.l.o.g. there are no name clashes between two projected constraints (9:C ) ^ (9 :D) Then we can shift all projection operators to the outermost level because we nd (9:C ) ^ (9 :D) =e 9:9 :C ^ D We can summarize this observation in the following lemma. Lemma 1 Let C in X. Then there exists a projection free constraint D such that C =e 9:D . The next lemma gives us a procedure to lift principal normal forms of constraints to arbitrary constraints. It states that whenever we can compute the principal normal form of a constraint D then we get the principal normal form of the constraint 9:D for free. Before we proceed, we state the following lemma which we will use in the next two lemmas. Lemma 2 Given a projection{free constraint C in X such that 9:C 2 S and C 62 S . Then there is a such that 9 :C =e [ = ]C . Proof: The only reason why C is not in solved 0form 00is that there is an unresolved uni cation problem ( = ) in C (there could also be more such unresolved uni cation problems). But because 9:C is in solved form we know that `e 9:( 0 =0 00 ). 00Thene we can0 conclude there is a such thate [ =]( = ) = 9:( = 00 ). Thus we nd 9 :C = [ = ]C .
be a substitution where 62 codom() [ dom(). If
(C; ) = normalize(D; ) then
(9:C; nfg ) = normalize(9:D; ) Proof: Given the principal normal form (C; ) of (D; ). We assume w.l.o.g. that 62 codom( ). It holds that [ =]D e`e 9:D where 62 fv( ). We can conclude that 9 :C ` nf g 9:D. We get that (9:C; nf g ) is a normal form of (9:D; ). 0 0 Assume now (C ; ) is another normal form of (9:D; ) and w.l.o.g. 62 codom( 0 ). Then we now that C 0 `e 0 (9 :D) =e 9 : 0 D. We distinguish the following two cases: Case 0 D 2 S : Then ( 0 D; 0 ) is a normal0 forme of 0(D; ). Because (C; ) is principal we get that D ` C and 0 . We can derive that C 0 `e 9 : 0 D `e 9 : 0 C `e 0 (9 :C ) which shows that (9:C; nf g ) is principal. Case 0 D 62 S : Because 9: 0 D 2 S we can apply Lemma 2. That means, there is a such 0that 90: 0 D =e [ = ]( 0 D). Note, it holds that [ = ] = [ =]. We get that C 0 `e 0 [ = ]D because (C; ) is principal we can follow that C 0 `e ( 0 [ = ])C 0 [ = ] But then we get that C `e 0 (9 :C ) nf g 0 which also shows that (9:C; nf g ) is principal.
The next lemma states that a normal form of a constraint exists if a normal form of the projected constraint exists. Lemma 4 Given a substitution where 62 codom() [ dom() and a projection{free constraint D in X. Then (D; ) has a normal form if (9:D; ) has a normal form. Proof: We assume that (C; ) is a normal form of (9:D; ). That means, C `e (9:D) Also, we know that 9: D =e (9:D). We distinguish the following two cases: Case D 2 S : Then it is easy to see that ( D; ) is a normal form of (D; ). Case D 62 S : Weeapply Lemma 2. We know there is a such that 9: D = ([ = ] )D. In this case (C; [ = ] ) is a normal form of (D; ).
We have now everything at hand to prove our main theorem. Given a normalize function that is only de ned for projection{free constraint problems then we can normalize arbitrary constraints. Especially, we nd that X satis es the principal constraint property.
The following lemma shows how to compute a principal normal form for arbitrary constraints given a procedure that computes the principal normal form of projection{free constraints. 14
Theorem 5 Given a function normalize that normalizes projection{ free constraints. Then the constraint system X satis es the principal constraint property. Proof: Given an arbitrary0 constraint problem (D; ) where e 0 D =
9 :D such that D is projection{free. We consider two cases. First, assume (D; ) has no normal form. Because of Lemma 4 we know that (D0 ; ) does not have a normal form either. Now, assume (D; ) does have a normal form. We apply Lemma 4 and nd that the normal form of 0(D0 ; ) exists. By assumption we know how to normalize (D ; ). That means (D0 ; ) does have a principal normal form and we can compute its principal normal form. With Lemma 3 we can lift the principal normal form of the projection{free constraint problem and obtain the principal normal form of (D; ) exists. We can conclude that X has the principal constraint property.
Remark 1 In Lemma 3 we implicitly imply that 9:C is in solved form. If S is closed under projection then0 this follows immediately. It holds that there must be a C 2 S such that C 0 =e0 9:C but in general it is not obvious how to determine C . It is de nitely decidable for all discussed applications in this paper. But in general we do not know the answer. Remark 2 It is important to point out that the proof of
the above theorem is not constructive. The problem is that Lemma 4 is only stated in one direction. We would need the other direction to give a constructive proof. It turns out that the other direction can be proven if the set S of solved forms is closed under projection, i.e. if C 2 S then 9:C 2 S . This is usally a conditions that holds for the set S .
15