Wadler
Wadler
1
This work grew out of the efforts of the Haskell arithmetic and equality in Standard ML and Mi-
committee to design a lazy functional programming randa.
language2 . One of the goals of the Haskell commit- Arithmetic. In the simplest approach to overload-
tee was to adopt “off the shelf” solutions to problems ing, basic operations such as addition and multiplica-
wherever possible. We were a little surprised to re- tion are overloaded, but functions defined in terms of
alise that arithmetic and equality were areas where them are not. For example, although one can write
no standard solution was available! Type classes 3*3 and 3.14*3.14, one cannot define
were developed as an attempt to find a better so-
lution to these problems; the solution was judged square x = x*x
successful enough to be included in the Haskell de- and then write terms such as
sign. However, type classes should be judged inde-
pendently of Haskell; they could just as well be in- square 3
corporated into another language, such as Standard square 3.14
ML.
This is the approach taken in Standard ML. (Inci-
Type classes appear to be closely related to issues dentally, it is interesting to note that although Stan-
that arise in object-oriented programming, bounded dard ML includes overloading of arithmetic opera-
quantification of types, and abstract data types tors, its formal definition is deliberately ambiguous
[CW85, MP85, Rey85]. Some of the connections are about how this overloading is resolved [HMT88, page
outlined below, but more work is required to under- 71], and different versions of Standard ML resolve
stand these relations fully. overloading in different ways.)
A type system very similar to ours has been dis- A more general approach is to allow the above
covered independently by Stefan Kaes [Kae88]. Our equation to stand for the definition of two over-
work improves on Kaes’ in several ways, notably loaded versions of square, with types Int -> Int
by the introduction of type classes to group re- and Float -> Float. But consider the function:
lated operators, and by providing a better transla-
tion method. squares (x, y, z)
This paper is divided into two parts: the body = (square x, square y, square z)
gives an informal introduction to type classes, while Since each of x, y, and z might, independently, have
the appendix gives a more formal description. Sec- either type Int or type Float, there are eight possi-
tion 2 motivates the new system by describing limi- ble overloaded versions of this function. In general,
tations of ad-hoc polymorphism as it is used in Stan- there may be exponential growth in the number of
dard ML and Miranda. Section 3 introduces type translations, and this is one reason why such solu-
classes by means of a simple example. Section 4 tions are not widely used.
illustrates how the example of Section 3 may be In Miranda, this problem is side-stepped by not
translated into an equivalent program without type overloading arithmetic operations. Miranda provides
classes. Section 5 presents a second example, the def- only the floating point type (named “num”), and
inition of an overloaded equality function. Section 6 there is no way to use the type system to indicate
describes subclasses. Section 7 discusses related work that an operation is restricted to integers.
and concludes. Appendix A presents inference rules
for typing and translation. Equality. The history of the equality operation is
checkered: it has been treated as overloaded, fully
polymorphic, and partly polymorphic.
2 Limitations of ad-hoc The first approach to equality is to make it over-
loaded, just like multiplication. In particular, equal-
polymorphism ity may be overloaded on every monotype that ad-
mits equality, i.e., does not contain an abstract type
This section motivates our treatment of ad-hoc poly- or a function type. In such a language, one may
morphism, by examining problems that arise with write 3*4 == 12 to denote equality over integers, or
2 The Haskell committee includes: Arvind, Brian Boutel, ’a’ == ’b’ to denote equality over characters. But
Jon Fairbairn, Joe Fasel, Paul Hudak, John Hughes, Thomas one cannot define a function member by the equations
Johnsson, Dick Kieburtz, Simon Peyton Jones, Rishiyur
Nikhil, Mike Reeve, Philip Wadler, David Wise, and Jonathan member [] y = False
Young. member (x:xs) y = (x == y) \/ member xs y
2
and then write terms such as dictionary of appropriate methods. This is exactly
the approach used in object-oriented programming
member [1,2,3] 2 [GR83].
member "Haskell" ’k’ In the case of polymorphic equality, this means
(We abbreviate a list of characters [’a’,’b’,’c’] that both arguments of the equality function will
as "abc".) This is the approach taken in the first contain a pointer to the same dictionary (since they
version of Standard ML [Mil84]. are both of the same type). This suggests that per-
A second approach is to make equality fully poly- haps dictionaries should be passed around indepen-
morphic. In this case, its type is dently of objects; now polymorphic equality would
be passed one dictionary and two objects (minus dic-
(==) :: a -> a -> Bool tionaries). This is the intuition behind type classes
and the translation method described here.
where a is a type variable ranging over every type.
The type of the member function is now
3
class Num a where
(+), (*) :: a -> a -> a
negate :: a -> a
instance Num Int where
(+) = addInt
(*) = mulInt
negate = negInt
instance Num Float where
(+) = addFloat
(*) = mulFloat
negate = negFloat
square :: Num a => a -> a
square x = x * x
squares :: Num a, Num b, Num c => (a,b,c) -> (a,b,c)
squares (x, y, z) = (square x, square y, square z)
4
This is read, “square has type a -> a, for every a Each term of the form x+y, x*y, and negate x is
such that a belongs to class Num (i.e., such that (+), now replaced by a corresponding term, as follows:
(*), and negate are defined on a).” We can now
x+y --> add numD x y
write terms such as
x*y --> mul numD x y
square 3 negate x --> neg numD x
square 3.14 where numD is an appropriate dictionary. How is the
and an appropriate type will be derived for each (Int appropriate dictionary determined? By its type. For
for the first expression, Float for the second). On example, we have the following translations:
the other hand, writing square ’x’ will yield a type 3 * 3
error at compile time, because Char has not been --> mul numDInt 3 3
asserted (via an instance declaration) to be a numeric
type. 3.14 * 3.14
Finally, if we define the function squares men- --> mul numDFloat 3.14 3.14
tioned previously, then the type given in Figure 1 As an optimisation, it is easy for the compiler to
will be inferred. This type may be read, “squares perform beta reductions to transform these into
has the type (a,b,c) -> (a,b,c) for every a, b, mulInt 3 3 and mulFloat 3.14 3.14, respectively.
and c such that a, b, and c belong to class Num”. If the type of a function contains a class, then this
(We write (a,b,c) for the type that is the cartesian is translated into a dictionary that is passed at run-
product of a, b, and c.) So squares has one type, time. For example, here is the definition of square
not eight. Terms such as with its type
squares (1, 2, 3.14) square :: Num a => a -> a
square x = x * x
are legal, and derive an appropriate type.
This translates to
5
class Eq a where
(==) :: a -> a -> bool
instance Eq Int where
(==) = eqInt
instance Eq Char where
(==) = eqChar
member :: Eq a => [a] -> a -> Bool
member [] y = False
member (x:xs) y = (x == y) \/ member xs y
instance Eq a, Eq b => Eq (a,b) where
(u,v) == (x,y) = (u == x) & (v == y)
instance Eq a => Eq [a] where
[] == [] = True
[] == y:ys = False
x:xs == [] = False
x:xs == y:ys = (x == y) & (xs == ys)
data Set a = MkSet [a]
instance Eq a => Eq (Set a) where
MkSet xs == MkSet ys = and (map (member xs) ys)
& and (map (member ys) xs)
The definition is summarised in Figure 3. We be- a and b such that a is in class Eq and b is in class Eq,
gin by declaring a class, Eq, containing a single op- the pair (a,b) is also in class Eq.” In other words,
erator, (==), and instances Eq Int and Eq Char of “if equality is defined on a and equality is defined on
this class. b, then equality is defined on (a,b).” The instance
We then define the member function in the usual defines equality on pairs in terms of equality on the
way, as shown in Figure 3. The type of member need two components, in the usual way.
not be given explicitly, as it can be inferred. The Similarly, it is possible to define equality over lists.
inferred type is: The first line of this instance reads, “if equality is
defined on a, then equality is defined on type ‘list of
member :: Eq a => [a] -> a -> Bool a’.” We may now write terms such as
This is read “member has type [a] -> a -> Bool, "hello" == "goodbye"
for every type a such that a is in class Eq [[1,2,3],[4,5,6]] == []
(i.e., such that equality is defined on a)” (This member ["Haskell", "Alonzo"] "Moses"
is exactly equivalent to the Standard ML type
which all evaluate to False.
’’a list->’’a->bool, where ’’a is an “eqtype
The final data declaration defines a new type con-
variable”.) We may now write terms such as
structor Set and a new value constructor MkSet. If
member [1,2,3] 2 a module exports Set but hides MkSet, then out-
member "Haskell" ’k’ side of the module the representation of Set will not
be accessible; this is the mechanism used in Haskell
which both evaluate to True. to define abstract data types. The final instance de-
Next, we give an instance defining equality over fines equality over sets. The first line of this instance
pairs. The first line of this instance reads, “for every reads, “if equality is defined on a, then equality is
6
data EqD a = EqDict (a -> a -> Bool)
eq (EqDict e) = e
eqDInt :: EqD Int
eqDInt = EqDict eqInt
eqDChar :: EqD Int
eqDChar = EqDict eqChar
member’ :: EqD a -> [a] -> a -> Bool
member’ eqDa [] y = False
member’ eqDa (x:xs) y = eq eqDa x y \/ member’ eqDa xs y
eqDPair :: (EqD a, EqD b) -> EqD (a,b)
eqDPair (eqDa,eqDb) = EqDict (eqPair (eqDa,eqDb))
eqPair :: (EqD a, EqD b) -> (a,b) -> (a,b) -> Bool
eqPair (eqDa,eqDb) (x,y) (u,v) = eq eqDa x u & eq eqDb y v
eqDList :: EqD a -> EqD [a]
eqDList eqDa = EqDict (eqList eqDa)
eqList :: EqD a -> [a] -> [a] -> Bool
eqList eqDa [] [] = True
eqList eqDa [] (y:ys) = False
eqList eqDa (x:xs) [] = False
eqList eqDa (x:xs) (y:ys) = eq eqDa x y & eq (eqDList eqDa) xs ys
defined on type ‘set of a’.” In this case, sets are rep- in Figure 3. The first part of the translation intro-
resented in terms of lists, and two sets are taken to duces nothing new, and is similar to the translation
be equal if every member of the first is a member in Section 4.
of the second, and vice-versa. (The definition uses We begin by defining a dicitionary EqD correspond-
standard functions map, which applies a function to ing to the class Eq. In this case, the class contains
every element of a list, and and, which returns the only one operation, (==), so the dictionary has only
conjunction of a list of booleans.) Because set equal- one entry. The selector function eq takes a dictio-
ity is defined in terms of member, and member uses nary of type EqD a and returns the one entry, of
overloaded equality, it is valid to apply equality to type a->a->Bool. Corresponding to the instances
sets of integers, sets of lists of integers, and even sets Eq Int and Eq Char we define two dictionaries of
of sets of integers. types EqD Int and EqD Char, containing the appro-
This last example shows how the type class mech- priate equality functions, and the function member
anism allows overloaded functions to be defined over is translated to member’ in a straightforward way.
abstract data types in a natural way. In particular, Here are three terms and their translations:
this provides an improvement over the treatment of 3*4 == 12
equality provided in Standard ML or Miranda. --> eq eqDInt (mul numDInt 3 4) 12
member [1,2,3] 2
5.1 Translation of equality --> member’ eqDInt [1,2,3] 2
We now consider how the translation mechanism ap- member "Haskell" ’k’
plies to the equality example. --> member’ eqDChar "Haskell" ’k’
Figure 4 shows the translation of the declarations The translation of the instance declaration for
7
equality over lists is a little trickier. Recall that the numerical and equality operations, then these each
instance declaration begins appear in the type separately:
As an optimisation, it is easy for the compiler to per- memsq :: Num a => [a]->a->Bool
form beta reductions to transform terms of the form
The qualifier Eq a no longer needs to be mentioned,
eq (eqDList eqD) into eqList eqD, where eqD is
because it is implied by Num a.
any dictionary for equality. This optimisation may
In general, each class may have any number of sub
be applied to the first two examples above, and also
or superclasses. Here is a contrived example:
to the definition of eqList itself in Figure 4.
It is worthwhile to compare the efficiency of this class Top a where
translation technique with polymorphic equality as fun1 :: a -> a
found in Standard ML or Miranda. The individual
class Top a => Left a where
operations, such as eqInt are slightly more efficient
fun2 :: a -> a
than polymorphic equality, because the type of the
argument is known in advance. On the other hand, class Top a => Right a where
operations such as member and eqList must explic- fun3 :: a -> a
itly pass an equality operation around, an overhead class Left a, Right a => Bottom a
that polymorphic equality avoids. Further experi- where
ence is needed to asses the trade-off between these fun4 :: a -> a
costs.
The relationships among these types can be dia-
grammed as follows:
6 Subclasses
Top
In the preceeding, Num and Eq were considered as / \
completely separate classes. If we want to use both / \
8
Left Right class Coerce a b where
\ / coerce :: a -> b
\ / instance Coerce Int Float where
Bottom coerce = convertIntToFloat
Although multiple superclasses pose some prob-
In this case, the assertion Coerce a b might be
lems for the usual means of implementing object-
taken as equivalent to the assertion that a is a sub-
oriented languages, they pose no problems for the
type of b. This suggests a relation between this work
translation scheme outlined here. The translation
and work on bounded quantification and on subtypes
simply assures that the appropriate dictionaries are
(see [CW85, Rey85] for excellent surveys of work in
passed at run-time; no special hashing schemes are
this area, and [Wan87, Car88] for more recent work).
required, as in some object-oriented systems.
Type classes may be thought of as a kind of
bounded quantifier, limiting the types that a type
7 Conclusion variable may instantiate to. But unlike other ap-
proaches to bounded quantification, type classes do
It is natural to think of adding assertions to the class not introduce any implicit coercions (such as from
declaration, specifying properties that each instance subtype Int to supertype Float, or from a record
must satisfy: with fields x, y, and z to a record with fields x and
y). Further exploration of the relationship between
class Eq a where type classes and these other approaches is likely to
(==) :: a -> a -> Bool be fruitful.
% (==) is an equivalence relation Type classes also may be thought of as a kind
class Num a where of abstract data type. Each type class specifies
zero, one :: a a collection of functions and their types, but not
(+), (*) :: a -> a -> a how they are to be implemented. In a way, each
negate :: a -> a type class corresponds to an abstract data type with
% (zero, one, (+), (*), negate) many implementations, one for each instance dec-
% form a ring laration. Again, exploration of the relationship be-
tween type classes and current work on abstract data
It is valid for any proof to rely on these properties, so types [CW85, MP85, Rey85] appears to be called for.
long as one proves that they hold for each instance We have already referred to the work of Kaes. One
declaration. Here the assertions have simply been advance of our work over his is the conceptual and
written as comments; a more sophisticated system notational benefit of grouping overloaded functions
could perhaps verify or use such assertions. This sug- into classes. In addition, our system is more gen-
gests a relation between classes and object-oriented eral; Kaes cannot handle overloadings involving more
programming of a different sort, since class declara- than one type variable, such as the coerce example
tions now begin to resemble object declarations in above. Finally, our translation rules are an improve-
OBJ [FGJM85]. ment over his. Kaes outlines two sets of translation
It is possible to have overloaded constants, such as rules (which he calls “semantics”), one static and one
zero and one in the above example. However, unre- dynamic. His dynamic semantics is more limited in
stricted overloading of constants leads to situations power than the language described here; his static
where the overloading cannot be resolved without semantics appears similar in power, but, unlike the
providing extra type information. For instance, the translation described here, can greatly increase the
expression one * one is meaningless unless it is used size of a program.
in a context that specifies whether its result is an Int One drawback of our translation method is that
or a Float. For this reason, we have been careful in it introduces new parameters to be passed at run-
this paper to use constants that are not overloaded: time, corresponding to method dictionaries. It may
3 has type Int, and 3.14 has type Float. A more be possible to eliminate some of these costs by us-
general treatment of constants seems to require co- ing partial evaluation [BEJ88] to generate versions
ercion between subtypes. of functions specialised for certain dictionaries; this
It is reasonable to allow a class to apply to more would reduce run time at the cost of increasing code
than one type variable. For instance, we might have size. Further work is needed to assess the trade-offs
9
between our approach (with or without partial eval- types in other expressions will be inferred by the
uation) and other techniques. rules given here.
It is clear from the above that many issues remain As an example, a portion of the definition of equal-
to be explored, and many tradeoffs remain to be as- ity given in Figure 3 is shown in Figure 6. In this
sessed. We look forward to the practical experience figure, and in the rest of this appendix, we use Eq τ
with type classes that Haskell will provide. as an abbreviation for the type τ → τ → Bool .
Acknowledgements. The important idea that As a second example, a portion of the definition
overloading might be reflected in the type of a func- of arithmetic operators given in Figure 1 is shown in
tion was suggested (in a rather different form) by Figure 7. In this figure we use Num τ as an abbrevi-
Joe Fasel. For discussion and comments, we are also ation for the type
grateful to: Luca Cardelli, Bob Harper, Paul Hudak, (τ → τ → τ, τ → τ → τ, τ → τ )
John Hughes, Stefan Kaes, John Launchbury, John
Mitchell, Kevin Mitchell, Nick Rothwell, Mads Tofte, In translating to the formal language, we have
David Watt, the members of the Haskell committee, grouped the three operators together into a “dictio-
and the members of IFIP 2.8. nary”. This is straightforward, and independent of
the central issue: how to resolve overloading.
over x :: σ in e A ⊢ e :: σ \ e
declares x to be an overloaded identifier. Within the This can be read as, “under the set of assumptions
scope of this declaration, there may be one or more A, the expression e has well-typing σ with transla-
corresponding inst expressions tion e”. Each typing also includes a translation, so
the rules derive typing\translation pairs. It is possi-
inst x :: σ ′ = e0 in e1 ble to present the typing rules without reference to
the translation, simply by deleting the ‘\e’ portion
where the type σ ′ is an instance of the type σ (a from all rules. It is not, however, possible to present
notion to be made precise later). Unlike lambda the translation rules independently, since typing con-
and let expressions, the bound variables in over and trols the translation. For example, the introduction
inst expressions may not be redeclared in a smaller and elimination of predicates in types controls the
scope. Also unlike lambda and let expressions, over introduction and elimination of lambda abstractions
and inst expressions must contain explicit types; the in translations.
10
Identifiers x
Expressions e ::= x
| e0 e1
| λx. e
| let x = e0 in e1
| over x :: σ in e
| inst x :: σ = e0 in e1
Type Variables α
Type Constructors χ
Types τ ::= (τ → τ ′ ) | α | χ(τ1 . . . τn )
Predicated Types ρ ::= (x :: τ ). ρ | τ
Type-schemes σ ::= ∀α. σ | ρ
over eq :: ∀α. Eq α in
inst eq :: Eq Int = eqInt in
inst eq :: Eq Char = eqChar in
inst eq :: ∀α.∀β.(eq :: Eq α).(eq :: Eq β).Eq (α, β)
= λp.λq. eq (fst p) (fst q) ∧ eq (snd p) (snd q) in
eq (1, ‘a’) (2, ‘b’)
11
(eq ::o ∀α.Eq α),
(eq ::i Eq Int \ eq (Eq Int ) ),
(eq ::i Eq Char \ eq (Eq Char ) ),
(eq ::i ∀α.∀β.(eq :: Eq α).(eq :: Eq β).Eq (α, β) \ eq (∀α.∀β.(eq::Eq α).(eq::Eq β).Eq (α,β)) ),
(eq :: Eq α \ eq (Eq α) ),
(eq :: Eq β \ eq (Eq β) ),
(p :: (α, β) \ p),
(q :: (α, β) \ q)
TAUT A, (x :: σ \ x) ⊢ x :: σ \ x
TAUT A, (x ::i σ \ x) ⊢ x :: σ \ x
A ⊢ e :: ∀α. σ \ e
SPEC
A ⊢ e :: [α \ τ ]σ \ e
A ⊢ e :: σ \ e
α not free in A
GEN
A ⊢ e :: ∀α. σ \ e
A ⊢ e :: (τ ′ → τ ) \ e
A ⊢ e′ :: τ ′ \ e′
COMB
A ⊢ (e e′ ) :: τ \ (e e′ )
Ax , (x :: τ ′ \ x) ⊢ e :: τ \ e
ABS
A ⊢ (λx. e) :: (τ ′ → τ ) \ (λx. e)
A ⊢ e :: σ \ e
Ax , (x :: σ \ x) ⊢ e′ :: τ \ e′
LET
A ⊢ (let x = e in e′ ) :: τ \ (let x = e in e′ )
12
A.3 Assumptions The instance relation
Typing is done in the context of a set of assump-
σ ≥A σ ′
tions, A. The assumptions bind typing and transla-
tion information to the free identifiers in an expres-
where σ = ∀α1 . . . αn . ρ and σ ′ = ∀β1 . . . βm . ρ′ , is
sion. This includes identifiers bound in lambda and
defined as follows:
let expression, and overloaded identifiers. Although
we write them as sequences, assumptions are sets, σ ≥A σ ′ iff
and therefore the order is irrelevant. (1) βi is not free in σ and
There are three forms of binding in an assumption (2) ∃τ1 , . . . , τn . [τ1 /α1 , . . . , τn /αn ]ρ ≥A ρ′
list:
This part is similar to the definition in Damas/
• (x ::o σ) is used for overloaded identifiers;
Milner. The bound variables of σ are specialised and
• (x ::i σ \ xσ ) is used for declared instances of the resulting predicated types are compared.
overloaded identifiers; and Define ρ ≥A ρ′ iff the type part of ρ equals the type
part of ρ′ (the same condition as Damas/Milner),
• (x :: σ \ x) is used for lambda and let bound and for every predicate (x :: τ ) in ρ, either
variables, and assumed instances of overloaded
identifiers. • there is a predicate of the form (x :: τ ) in ρ′ (i.e.
the predicate appears in both types); or
In (x :: σ \ x) and (x ::i σ \ x), the identifier x is the
translation of x. If x is not an overloaded identifier
• the predicate can be eliminated under assump-
(that is, if x is bound by a lambda or let expression),
tions A.
then the assumption for x has the form (x :: σ \ x),
so x simply translates as itself.
A predicate (x :: τ ) can be eliminated under A iff
Figure 8 shows the assumptions available when ap-
either
plying the inference rules to the expression
• (x :: τ \ x) is in A; or
λp. λq. eq (fst p) (fst q) ∧ eq (snd p) (snd q)
13
A, (x :: τ \ xτ ) ⊢ e :: ρ \ e
PRED (x ::o σ) ∈ A
A ⊢ e :: (x :: τ ). ρ \ (λxτ . e)
A ⊢ e :: (x :: τ ). ρ \ e
A ⊢ x :: τ \ e′
REL (x ::o σ) ∈ A
A ⊢ e :: ρ \ (e e′ )
Ax , (x ::o σ) ⊢ e :: τ \ e
OVER
A ⊢ (over x :: σ in e) :: τ \ e
A, (x ::i σ ′ \ xσ′ ) ⊢ e′ :: σ ′ \ e′
A, (x ::i σ ′ \ xσ′ ) ⊢ e :: τ \ e
INST (x ::o σ) ∈ A
A ⊢ (inst x :: σ ′ = e′ in e) :: τ \ (let xσ′ = e′ in e)
14
A.5 Valid assumptions For example, let A0 be the set of assumptions
shown in Figure 8, together with assumptions about
All sets of assumptions used within proofs must be
the types of integer and character constants. Then
valid. The valid sets of assumptions are inductively
the above rules are sufficient to derive that
defined as follows:
A0 ⊢ (eq 1 2) :: Bool \ (eq (Eq Int ) 1 2)
• Empty. The empty assumption set, {}, is valid.
A0 ⊢ (eq ‘a’ ‘b’) :: Bool \ (eq (Eq Char ) ‘a’ ‘b’)
• Normal identifier. If A is a valid assumption set,
x is an identifier that does not appear in A, and That is, these rules alone are sufficient to resolve
σ is a type scheme, then simple overloading.
More complicated uses of overloading require the
A, (x :: σ \ x) remaining four rules, shown in Figure 10. The first
two deal with the introduction and elimination of
is a valid assumption set. predicates, and the second two deal with the over
and inst constructs.
• Overloaded identifier. If A is a valid assumption As we have seen, expressions with types that con-
set, x is an identifier that does not appear in A, tain classes (that is, expressions with predicated
σ is a type scheme, and τ1 , . . . , τm are types and types) are translated to lambda abstractions that
σ1 , . . . , σn are types schemes such that require a dictionary to be passed at run-time. This
– σ ≥A σi , for i from 1 to n, and idea is encapsulated in the PRED (“predicate”) and
REL (“release”) rules. The PRED and REL rules
– σ ≥A τi , for i from 1 to m, and introduce and eliminate predicates analogously to
– σi #σj , for distinct i, j from 1 to n the way that the GEN and SPEC rules introduce
and eliminate bound type variables. In particular,
then the PRED rule adds a predicate to a type (and has
a lambda expression as its translation) and the REL
A, (x ::o σ), rule removes a predicate from a type (and has an ap-
(x ::i σ1 \ xσ1 ), . . . , (x ::i σn \ xσn ), plication as its translation).
(x :: τ1 \ xτ1 ), . . . , (x :: τm \ xτm ) The OVER rule types over expressions adding
the appropriate (::o ) binding to the environment,
is a valid assumption set.
and the INST rule types inst expressions adding
For example, the assumptions in Figure 8 are a the appropriate (::i ) binding to the environment.
valid set. However, this set would be invalid if aug- The validity condition on sets of assumptions ensures
mented with the binding that overloaded identifiers are only instanced at valid
types.
(eq ::i ∀γ.Eq (Char , γ) \ eq (∀γ.Eq (Char ,γ)) ) Notice that none of the translations contain over
or inst expressions, therefore, they contain no over-
as this instance overlaps with one already in the set. loading. It is easy to verify that the translations are
themselves well-typed in the Hindley/Milner system.
A.6 Inference rules For example, the program in Figure 6 is translated
by these rules into the program in Figure 11. The
We now give inference rules that characterise well- reader can easily verify that this corresponds to the
typings of the form translation from Figure 3 to Figure 4. We have thus
shown how to formalise the typing and transforma-
A ⊢ e :: σ \ e tion ideas that were presented informally in the body
of the paper.
The rules break into two groups, shown in Figures 9
and 10. The first group is based directly on the
Damas/Milner rules (Figure 9). There are two small A.7 Principal typings
differences: translations have been added to each rule Given A and e, we call σ a principal type scheme for
in a straightforward way, and there are two TAUT e under A iff
rules instead of one (one rule for (::) bindings and
one for (::i ) bindings). • A ⊢ e :: σ \ e; and
15
• for every σ ′ , if A ⊢ e :: σ ′ \ e′ then σ ≥A σ ′ [DM82] L. Damas and R. Milner, Principal type
schemes for functional programs. In Pro-
A key result in the Hindley/Milner system is that ceedings of the 9’th Annual Symposium
every expression e that has a well-typing has a prin- on Principles of Programming Languages,
cipal type scheme. Albuquerque, N.M., January 1982.
We conjecture that for every valid set of assump-
tions A and every expression e containing no over or [FGJM85] K. Futasagi, J.A. Goguen, J.-P. Jouan-
inst expressions, if e has a well-typing under A then naud, and J. Meseguer, Principles of
e has a principal type scheme under A. OBJ2. In Proceedings of the 12’th An-
For example, let A0 be the set of assumptions in nual Symposium on Principles of Pro-
Figure 8. Then the typing gramming Languages, January 1985.
But there is no principal type! One possible resolu- [Kae88] S. Kaes, Parametric polymorphism. In
tion of this is to require that over and inst declara- Proceedings of the 2’nd European Sym-
tions have global scope. It remains an open question posium on Programming, Nancy, France,
whether there is some less drastic restriction that March 1988. LNCS 300, Springer-Verlag,
still ensures the existence of principal types. 1988.
[Mil78] R. Milner, A theory of type polymor-
phism in programming. J. Comput. Syst.
References Sci. 17, pp. 348–375, 1978.
[BEJ88] D. Bjørner, A. Ershov, and N.D. Jones, [Mil84] R. Milner, A proposal for Standard ML.
editors, Partial Evaluation and Mixed In Proceedings of the Symposium on Lisp
Computation, North-Holland, 1988 (to and Functional Programming, Austin,
appear). Texas, August 1984.
[CW85] L. Cardelli and P. Wegner, On under- [Mil87] R. Milner, Changes to the Standard ML
standing types, data abstraction, and core language. Report ECS-LFCS-87-33,
polymorphism. Computing Surveys 17, 4, Edinburgh University, Computer Science
December 1985. Dept., 1987.
[Car88] L. Cardelli, Structural subtyping and the [MP85] J. C. Mitchell and G. D. Plotkin, Ab-
notion of power type. In Proceedings of stract types have existential type. In Pro-
the 15’th Annual Symposium on Prin- ceedings of the 12’th Annual Symposium
ciples of Programming Languages, San on Principles of Programming Languages,
Diego, California, January 1988. January 1985.
16
[Rey85] J. C. Reynolds, Three approaches to
type structure. In Mathematical Foun-
dations of Software Development, LNCS
185, Springer-Verlag, 1985.
[Str67] C. Strachey, Fundamental concepts in
programming languages. Lecture notes
for International Summer School in Com-
puter Programming, Copenhagen, Au-
gust 1967.
[Tur85] D. A. Turner, Miranda: A non-strict
functional language with polymorphic
types. In Proceedings of the 2’nd Inter-
national Conference on Functional Pro-
gramming Languages and Computer Ar-
chitecture, Nancy, France, September
1985. LNCS 201, Springer-Verlag, 1985.
[Wan87] M. Wand, Complete type inference for
simple objects. In Proceedings of the Sym-
posium on Logic in Computer Science,
Ithaca, NY, June 1987. IEEE Computer
Society Press, 1987.
17