Ad-Hoc Polymorphism and Dynamic Typing in a Statically Typed Functional Language
Total Page:16
File Type:pdf, Size:1020Kb
Ad-hoc Polymorphism and Dynamic Typing in a Statically Typed Functional Language ThomasvanNoort PeterAchten RinusPlasmeijer Institute for Computing and Information Sciences, Radboud University Nijmegen P.O. Box 9010, 6500 GL, Nijmegen, The Netherlands {thomas, p.achten, rinus}@cs.ru.nl Abstract unwrapped by pattern matching and providing a required type. Static typing in functional programming languages such as Clean, Although type unification can fail at run time when a dynamic value Haskell, and ML is highly beneficial: it prevents erroneous be- presents an unexpected type, the static type system guarantees that haviour at run time and provides opportunities for optimisations. when pattern matching succeeds, the unwrapped value can be used However, dynamic typing is just as important as sometimes types safely. are not known until run time. Examples are exchanging values be- While many dispute over choosing either static or dynamic typ- tween applications by deserialisation from disk, input provided by ing, we agree that the solution lies in the middle (Meijer and Dray- a user, or obtaining values via a network connection. Ideally, a ton, 2004): static typing system works in close harmony with an orthogonal dynamic typing system; not discriminating between statically and “Static typing where possible, dynamic typing when needed” dynamically typed values. In contrast to Haskell’s minimal sup- port for dynamic typing, Clean has an extensive dynamic typing; it We believe that a statically typed language is the starting point, adopted ML’s support for monomorphism and parametric polymor- extended with an escape to type values dynamically. Ideally, the phism and added the notion of type dependencies. Unfortunately, dynamic typing system is orthogonal to the static typing system, ad-hoc polymorphism has been left out of the equation over the imposing no restrictions on the value or types that can be consid- years. While both ad-hoc polymorphism and dynamic typing have ered dynamic. been studied in-depth earlier, their interaction in a statically typed Haskell has minimal support for dynamic typing, it only sup- functional language has not been studied before. In this paper we ports monomorphism (Baars and Swierstra, 2002; Cheney and explore the design space of their interactions. Hinze, 2002). Clean, on the other hand, has a rich and mature dynamic typing system; it adopted ML’s support for monomor- Categories and Subject Descriptors D.3.3 [Programming Lan- phism (Abadi et al., 1991; Pil, 1997) and parametric polymor- guages]: Language Constructs and Features phism (Abadi et al., 1994; Leroy and Mauny, 1993). Additionally, it includes the notion of type dependencies (Pil, 1999). Even generic General Terms Design, Languages functions can be applied to dynamic values (Wichers Schreur and Keywords ad-hoc polymorphism, dynamic typing Plasmeijer, 2005). Though, the quest for an orthogonal dynamic typing system cannot be completed without proper support for an- 1. Introduction other important concept: ad-hoc polymorphism. Ad-hoc polymorphism provides an abstraction mechanism to Static typing is the cornerstone of functional programming lan- parameterise values with behaviour. The usual suspects are func- guages such as Clean, Haskell, and ML. It prevents erroneous be- tions for the equality and ordering of values. Whereas in ML ad- haviour at run time by verifying type safety at compile time. Also, it hoc polymorphism is modelled via the module system (Wehr and provides opportunities for optimisations by exploiting either user- Chakravarty, 2008), Haskell and Clean model ad-hoc polymor- specified or inferred type information statically. phism via type classes which is resolved to a dictionary-passing However, sometimes the type of a value is not known until run style at compile time (Peterson and Jones, 1993; Wadler and Blott, time. Typically this is the case when interacting with the ‘outside’ 1989). Static type information is crucial in this approach; it is the world: exchanging values between applications by deserialisation driving force behind the translation mechanisms. from disk, input provided by a user, or obtaining values via a Although both ad-hoc polymorphism and dynamic typing have network connection. In such cases, dynamic typing is required been studied in-depth earlier, their interaction in a statically typed to defer type unification until run time. Values are wrapped in functional language has not been explored before. We identify two a uniform black box, as their type is statically not known, and sides to their interaction. On the one hand, it involves dynamic typ- ing in the world of ad-hoc polymorphism. For instance, a sorting function applied to a list of dynamically typed values that are ob- tained by deserialisation from disk or provided by a user as input. Permission to make digital or hard copies of all or part of this work for personal or Obviously, this poses a challenge since ad-hoc polymorphism isre- 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 solved at compile time while the type of dynamic values is known on the first page. To copy otherwise, to republish, to post on servers or to redistribute only at run time. Typically, this is solved by enumerating all ex- to lists, requires prior specific permission and/or a fee. pected types by hand. This resolves ad-hoc polymorphism statically WGP’10, September 26, 2010, Baltimore, Maryland, USA. but is cumbersome, prone to errors, and does not scale for evident Copyright c 2010 ACM 978-1-4503-0251-7/10/09. $10.00 reasons. On the other hand, the interaction concerns ad-hoc poly- :: DictEq α = {eq :: α → α → Bool } morphism in the world of dynamic typing. For example, a sorting function that is deserialised from disk and applied to some statically dictEqInt :: DictEq Int typed value. Here, the challenge is to extend the existing dynamic dictEqInt = {eq = λx y → eqInt x y } typing mechanisms to support ad-hoc polymorphic values. dictEqList :: ∀ α . DictEq α → DictEq [α] In this paper we explore the design space of the interactions and dictEqList da = provide a thorough intuition of the issues involved. While there is {eq = λx y → dictEqInt.eq (length x)(length y) a plethora of type class extensions (Peyton Jones et al., 1997), we . first only consider type classes in the style of Haskell 98 (Peyton ∧ and (zipWith da eq x y)} Jones, 2003). We set the scene by giving an overview of both dictEqPair :: conventional ad-hoc polymorphism via type classes in Clean and ∀ α β . DictEq α → DictEq β → DictEq (α, β) Haskell (Section 2), and dynamic typing in Clean (Section 3). Our dictEqPair da db = contributions are the following: {eq = λx y → da.eq (fst x)(fst y) • We describe two complementary approaches to dynamic typing ∧ db.eq (snd x)(snd y)} in ad-hoc polymorphism (Section 4): container datatypes and dynamic dictionary composition. :: DictOrd α = {lt :: α → α → Bool , dictEq :: DictEq α} • We describe two different approaches to ad-hoc polymorphism in dynamic typing (Section 5): dictionary-passing types and dictOrdInt :: DictOrd Int type code extension. dictOrdInt = {lt = λx y → ltInt x y , dictEq dictEqInt • We discuss how several type class extensions affect both sides = } of the interaction (Section 6). dictOrdList :: ∀ α . DictOrd α → DictOrd [α] dictOrdList da = Finally, we elaborate on related work (Section 7) and conclude {lt = λx y → dictOrdInt.lt (length x)(length y) with a brief discussion and future work (Section 8). ∨ or (zipWith da.lt x y) The examples given in this paper are defined using Clean syn- tax. While types in Clean have an explicit arity, we curry the , dictEq = dictEqList da.dictEq } types for the sake of presentation. An overview of syntactical and dictOrdPair :: semantical differences between Clean and Haskell is given else- ∀ α β . DictOrd α → DictOrd β → DictOrd (α, β) where (Achten, 2007; Van Groningen et al., 2010). dictOrdPair da db = {lt = λx y → da.lt (fst x)(fst y) 2. Ad-hoc polymorphism ∨ db.lt (snd x)(snd y) We give a brief overview of type classes (Section 2.1) and their , dictEq = dictEqPair da.dictEq db.dictEq } translation to dictionary-passing style (Section 2.2). Figure 1. Dictionary-passing style translation of Eq and Ord 2.1 Type classes Consider the following type class for the equality of values: that every Ord instance is also an Eq instance. Again, we define class Eq α where instances for integers, lists, and pairs: eq :: α → α → Bool instance Ord Int where The type class Eq has one member, the equality function. We lt x y = ltInt x y ignore default members for simplicity reasons; these are irrelevant instance Ord [α] | Ord α where to our approach and only clutter the examples. We provide several lt x y = lt (length x)(length y) ∨ or (zipWith lt x y) instances for this type class: instance Ord (α, β) | Ord α & Ord β where instance Eq Int where lt x y = lt (fst x)(fst y) ∨ lt (snd x)(snd y) eq x y = eqInt x y We assume the presence of a core function ltInt for the ordering of instance Eq [α] | Eq α where integers. Similar to the instance for Eq, the instances for lists and eq x y = eq (length x)(length y) ∧ and (zipWith eq x y) pairs require instances for their element types. Admittedly, not all instance Eq (α, β) | Eq α & Eq β where of these instances are useful in practice. Here, they merely serve eq x y = eq (fst x)(fst y) ∧ eq (snd x)(snd y) the purpose of illustrating the dictionary-passing style translation. A typical use of the ordering type class is a sorting function: We assume that in the instance for integers, a core equality function sort α .