How to Make Ad-Hoc Polymorphism Less Ad Hoc
Total Page:16
File Type:pdf, Size:1020Kb
How to make ad-hoc polymorphism less ad hoc Philip Wadler and Stephen Blott University of Glasgow∗ October 1988 Abstract integers and a list of floating point numbers. One widely accepted approach to parametric This paper presents type classes, a new approach polymorphism is the Hindley/Milner type system to ad-hoc polymorphism. Type classes permit over- [Hin69, Mil78, DM82], which is used in Standard loading of arithmetic operators such as multiplica- ML [HMM86, Mil87], Miranda1[Tur85], and other tion, and generalise the “eqtype variables” of Stan- languages. On the other hand, there is no widely dard ML. Type classes extend the Hindley/Milner accepted approach to ad-hoc polymorphism, and so polymorphic type system, and provide a new ap- its name is doubly appropriate. proach to issues that arise in object-oriented pro- This paper presents type classes, which extend the gramming, bounded type quantification, and ab- Hindley/Milner type system to include certain kinds stract data types. This paper provides an informal of overloading, and thus bring together the two sorts introduction to type classes, and defines them for- of polymorphism that Strachey separated. mally by means of type inference rules. The type system presented here is a generalisa- tion of the Hindley/Milner type system. As in that system, type declarations can be inferred, so explicit 1 Introduction type declarations for functions are not required. Dur- ing the inference process, it is possible to translate a ad-hoc parametric Strachey chose the adjectives and program using type classes to an equivalent program polymorphism to distinguish two varieties of [Str67]. that does not use overloading. The translated pro- Ad-hoc polymorphism occurs when a function is grams are typable in the (ungeneralised) Hindley/ defined over several different types, acting in a dif- Milner type system. ferent way for each type. A typical example is The body of this paper gives an informal introduc- overloaded multiplication: the same symbol may be tion to type classes and the translation rules, while 3*3 used to denote multiplication of integers (as in ) an appendix gives formal rules for typing and trans- and multiplication of floating point values (as in lation, in the form of inference rules (as in [DM82]). 3.14*3.14 ). The translation rules provide a semantics for type Parametric polymorphism occurs when a function classes. They also provide one possible implementa- is defined over a range of types, acting in the same tion technique: if desired, the new system could be way for each type. A typical example is the length added to an existing language with Hindley/Milner function, which acts in the same way on a list of types simply by writing a pre-processor. ∗Authors’ address: Department of Computing Science, Two places where the issues of ad-hoc polymor- University of Glasgow, Glasgow G12 8QQ, Scotland. Elec- phism arise are the definition of operators for arith- tronic mail: wadler, [email protected]. metic and equality. Below we examine the ap- Published in: 16’th ACM Symposium on Principles of Pro- proaches to these three problems adopted by Stan- gramming Languages, Austin, Texas, January 1989. dard ML and Miranda; not only do the approaches Permission to copy without fee all or part of this material is differ between the two languages, they also differ granted provided that the copies are not made or distributed within a single language. But as we shall see, type for direct commercial advantage, the ACM copyright notice classes provide a uniform mechanism that can ad- and the title of the publication and its date appear, and no- tice is given that copying is by permission of the Association dress these problems. for Computing Machinery. To copy otherwise, or to republish, requires a fee and/or specific permission. 1Miranda is a trademark of Research Software Limited. 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. 2 language . 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 2The 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 member :: [a] -> a -> Bool 3 An introductory example (We write [a] for the type “list of a”.) This means We will now introduce type classes by means of an that applying equality to functions or abstract types example. does not generate a type error. This is the approach Say that we wish to overload (+), (*), and negate taken in Miranda: if equality is applied on a func- (unary minus) on types Int and Float.