A Functional Notation for Functional Dependencies

Functional dependencies help resolve many of the ambiguities that result from the use of multi-parameter type classes. They effectively enable writing programs at the type-level which significantly enhances the expressive power of Haskell’s type system. Among the applications of this technique are the emulation of dependent types, and precise typechecking for XML and HTML combinator libraries. Unfortunately, the notation presently used for functional dependencies implies that the type-level programs are logic programs, but many of its applications are conceptually functional programs. We propose an alternative notation for functional dependencies which adds a functional-programming notation to Haskell’s type classes and makes applications of functional dependencies significantly more readable. We apply the new notation to our examples and study the problems arising due to Haskell’s open world assumption and overlapping instances. Since the invention of type classes more than a decade ago [12], every new year has seen astonishing new applications and interesting extensions of the original idea. The most recent addition is Jones’s incorporation of functional dependencies [8] which allow the formulation of tighter constraints on the instances 1 Email: neubauer@informatik.uni-freiburg.de 2 Email: thiemann@informatik.uni-freiburg.de 3 Email: gasbichl@informatik.uni-tuebingen.de 4 Email: sperber@informatik.uni-tuebingen.de 101 Gasbichler, Neubauer, Sperber, Thiemann of multi-parameter type classes. A functional dependency is a declaration which specifies that a set of parameters of a type class uniquely determines another parameter. Such a specification avoids many ambiguous typings. Functional dependencies constrain the instance declarations so that they specify a function at the type level. This functionality makes it feasible to write regular programs entirely evaluated by the type checker, opening the door for a wide range of potential applications [6,9]. Unfortunately, the use of functional dependencies is hampered by the traditional notation of type classes as relations and instances as logic programs computing these relations: As the resulting programs are getting more complex, they often become awkward and hard to read. This is not a fault of the mechanism of functional dependencies per se, but of the syntax offered by current implementations. Thus, in this paper, we suggest a notational shift for type classes: View a type class as a type-level function instead of a type-level relation. We offer some realistic example applications of functional dependencies which would benefit significantly from such a notation. Moreover, we give a brief formal outline of how the new notation may be implemented by a translation to the traditional syntax. We present the relevant part of a larger example—type-safe combinator libraries for XML documents—and discuss some of the problems remaining. 1 Simulating Dependent Types We start our investigation with two simple examples drawn from the realm of dependent types: formatted printing and specified list operations. 1.1 A type-safe sprintf The sprintf function from the C-library is an example of an unsafe function that can be made type-safe by using a dependent type [1]: The first parameter of sprintf is a format specifier which determines the number and type of the remaining parameters. The idea here is that a type-level function computes the type of the remaining parameters from the format specifier. For this to work in Haskell, format specifiers cannot simply be strings with control characters but rather String constants or values from the following datatypes: 5 data I f = I f data C f = C f data S f = S String f Using these datatypes, the format specifier S "Int: " (I (S ", Char: " (C "."))) 5 Danvy [5] has shown that this particular example can be made to work relying only on ML-style polymorphism.