A Theory of Overloading Peter J. Stuckey and Martin Sulzmann ∗ Department of Computer Science and Software Engineering The University of Melbourne, Vic. 3010, Australia {pjs,sulzmann}@cs.mu.oz.au Abstract We present a minimal extension of the Hindley/Milner system to al- low for overloading of identifiers. Our approach relies on a combi- nation of the HM(X) type system framework with Constraint Han- dling Rules (CHRs). CHRs are a declarative language for writing incremental constraint solvers. CHRs allow us to precisely describe the relationships among overloaded identifiers. Under some suffi- cient conditions on the CHRs we achieve decidable type inference and the semantic meaning of programs is unambiguous. Our ap- proach allows us to combine open and closed world overloading. We also show how to deal with overlapping definitions. Categories and Subject Descriptors D.3.2 [Programming Languages]: Language Classifications— Applicative (functional) languages; D.3.3 [Programming Lan- guages]: Language Constructs and Features—Polymorphism; F.3.3 [Logics and Meanings of Programs]: Studies of Program Con- structs—Type structure General Terms Languages, Theory Keywords overloading, type classes, type inference, constraints 1 Introduction The study of overloading, a.k.a. ad-hoc polymorphism, in the con- text of the Hindley/Milner system [23] dates back to Kaes [21], Wadler and Blott [35]. Since then, it became a powerful program- ming feature in languages such as Haskell [27], Mercury [13, 14] , HAL [6] and Clean [28]. In particular, Haskell provides through its type-class system [15] one of the most powerful overloading ∗ Current address: School of Computing, National University of Singapore, martin@comp.nus.edu.sg Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. ICFP’02, October 4-6, 2002, Pittsburgh, Pennsylvania, USA. Copyright 2002 ACM 1-58113-487-8/02/0010 ...$5.00 mechanisms. There have been a number of significant extensions of Haskell’s type class mechanism such as constructor classes [17], multi-parameter classes [20] and most recently functional depen- dencies [19]. Each of these extensions required a careful rein- vestigation of essential properties such as decidable type inference and coherent semantics. There is also a significant body of further closely related work, for example [10, 29, 4, 26, 24, 5]. Here, we present a minimal extension of the Hindley/Milner system to allow for overloading of identifiers. EXAMPLE 1. Consider the following type-annotated program where we provide definitions for overloaded functions leq and ins. overload leq :: Int → Int → Bool leq = primLeqInt overload leq :: Float → Float → Bool leq = primLeqFloat overload ins :: ∀a.Leq (a → a → Bool ) ⇒ [a] → a → [a] ins = let insList [] y = [y] insList (x:xs) y = if leq x y then x:(insList xs y) else y:x:xs in insList We assume that primLeqInt (primLeqFloat) is a primitive function, testing for less-than-equal on integers (floats). Note that the definition of ins depends on leq. This is reflected in ins type where we find the constraint Leq (a → a → Bool ). For simplicity, we omit further definitions of ins on other data structures such as trees etc. The novelty of our approach is that relationships among overloaded identifiers are defined in terms of the meta-language of Constraint Handling Rules (CHRs) [7]. In case of the above example, we find the following set of CHR simplification rules: (Leq1) Leq (Int → Int → Bool ) ⇐⇒ True (Leq2) Leq (Float → Float → Bool ) ⇐⇒ True (Ins1) Ins ([a] → a → [a]) ⇐⇒ Leq (a → a → Bool ) Rule (Leq1) states that leq is defined on type Int → Int → Bool . A similar property is stated by rule (Leq2). Rule (Ins1) states that ins on type [a] → a → [a] is defined iff leq is defined on type a → a → Bool . Logically, the ⇐⇒ symbol states an if-and-only-if relation. Operationally, a simplification rule can be read as follows. Whenever there is a term which matches the left-hand side, then this term can be simplified (replaced) by the right-hand side. CHRs also allow us to impose stronger constraints on the set of overloaded definitions via propagation rules: (Leq3) Leq (Int → Int → a) = ⇒ a = Bool (Ins2) Ins ([a] → b → c) = ⇒ b = a, c =[a]