Type Classes an Exploration of the Design Space

Total Page:16

File Type:pdf, Size:1020Kb

Type Classes� an Exploration of the Design Space Type classes an exploration of the design space Simon Peyton Jones Mark Jones University of Glasgow and Oregon Graduate Institute University of Nottingham Erik Meijer University of Utrecht and Oregon Graduate Institute May Abstract to Haskell can b e a painful exp erience One feature that is particularly often missed is multiparameter type classes Section explains why When type classes were rst introduced in Haskell they were regarded as a fairly exp erimental language feature and The obvious question is whether there is an upward therefore warranted a fairly conservative design Since that compatible way to extend Haskells class system to enjoy time practical exp erience has convinced many programmers some or all of the expressiveness that Gofer provides and of the b enets and convenience of type classes However on p erhaps some more b esides The main b o dy of this pap er o ccasion these same programmers have discovered examples explores this question in detail It turns out that there are where seemingly natural applicatio ns for type class overload a number of interlocking design decisions to b e made Gofer ing are prevented by the restrictions imp osed by the Haskell and Haskell each embo dy a particular set but it is very design useful to tease them out indep endently and see how they interact Our goal is to explore the design space as clearly It is p ossible to extend the type class mechanism of Haskell as p ossible laying out the choices that must b e made and in various ways to overcome these limitations but such pro the factors that aect them rather than prescribing a par p osals must b e designed with great care For example sev ticular solution Section We nd that the design space eral dierent extensions have b een implemented in Gofer is rather large we identify nine separate design decisions Some of these particularly the supp ort for multiparameter each of which has two or more p ossible choices though not classes have proved to b e very useful but interactions b e all combinations of choices make sense In the end however tween other asp ects of the design have resulted in a type we do oer our own opinion ab out a sensible set of choices system that is b oth unsound and undecidable Another illus Section tration is the introduction of constructor classes in Haskell which came without the prop er generalizatio n of the no A new language feature is only justiable if it results in a tion of a context As a consequence certain quite reasonable simplicati on or unication of the original language design programs are not typable or if the extra expressiveness is truly useful in practice One contribution of this pap er is to collect together a fairly large In this pap er we review the rationale b ehind the design of set of examples that motivate various extensions to Haskells Haskells class system we identify some of the weaknesses type classes in the current situation and we explain the choices that we face in attempting to remove them Why multiparameter type classes Introduction The most visible extension to Haskell type classes that we discuss is supp ort for multiparameter type classes The p os Type classes are one of the most distinctive features of sibility of multiparameter type classes has b een recognised Haskell Hudak et al They have b een used for an since the original pap ers on the sub ject Kaes Wadler 1 impressive variety of application s and Haskell signif Blott and Gofer has always supp orted them icantly extended their expressiveness by introducing con structor classes Jones a This section collects together examples of multiparameter type classes that we have encountered None of them are All programmers want more than they are given and many new none will b e surprising to the cognescenti and many p eople have bump ed up against the limitation s of Haskells have app eared inter alia in other pap ers Our purp ose in class system Another language Gofer Jones that collecting them is to provide a shared database of motivating has developed in parallel with Haskell enjoys a much more examples We would welcome new contributions lib eral and expressive class system This expressiveness is denitely b oth useful and used and transferring from Gofer 1 The current iteration of the Haskell language is Haskell but it is identical to Haskell in all resp ects relevant to this pap er Overloading with coupled parameters newtype Env e a Env e a instance ReaderMonad Env e e where Concurrent Haskell Peyton Jones Gordon Finne introduces a number of types such as mutable variables Work in Glasgow and the Oregon Graduate Institute MutVar synchronised mutable variables MVar channel on hardware description languages has led to class dec variables CVar communication channels Channel and skip larations similar to this channels SkipChan all of which come with similar op era tions that take the form class Monad ct Hard ct sg where newX a IO X a const a ct sg a getX X a IO a op a b sg a ct sg b putX X a a IO op a b c sg a sg b ct s c where X ranges over MVar etc Here are similar op erations instance Hard NetCircuit NetSignal where in the standard state monad instance Hard SimCircuit SimSignal where newST a ST s MutableVar s a Here the circuit constructor ct is a monad while getST MutableVar s a ST s a the signal constructor sg serves to distinguish values putST MutableVar s a a ST s available at circuitconstruction time of type Int say These are manifestly candidates for overloading yet a single from those owing along the wires at circuitexecution parameter type class cant do the trick The trouble is that time of type SimSignal Int say Each instance of in each case the monad type and the reference type come as Hard gives a dierent interpretation of the circuit for a pair IO MutVar and ST s MutableVar s What we example one might pro duce a net list while another want is a multiple parameter class that abstracts over b oth might simulate the circuit Like the VarMonad example the instance type come as class Monad m VarMonad m v where a pair it would make no sense to give an instance for new a m v a Hard NetCircuit SimSignal get v a m a put v a a m 2 The Haskell prelude denes denes the following two functions for reading and writing les instance VarMonad IO MutVar where instance VarMonad ST s MutableVar s where readFile FilePath IO String writeFile FilePath String IO This is quite a common pattern in which a twoparameter type class is needed b ecause the class signature is really over Similar functions can b e dened for many more pairs a tuple of types and where instance declarations capture di of device handles and communicatable types such as rect relationshi ps b etween sp ecic tuples of type construc mice buttons timers windows rob ots etc tors We call this overloading with coupled parameters Here are a number of other examples we have collected readMouse Mouse IO MouseEvent readButton Button IO readTimer Timer IO Float The class StateMonad Jones carries the state around naked instead of inside a container as in the sendWindow Window Picture IO VarMonad example sendRobot Robot Command IO sendTimer Timer Float IO class Monad m StateMonad m s where getS m s These functions are quite similar to the metho ds putS s m get VarMonad r m r a m a and put VarMonad r m r a a m of the VarMonad Here the monad m carries along a state of type s getS family except that here the monad m is xed to IO and extracts the state from the monad and putS overwrites the choice of the value type a is coupled with the b ox the state with a new value One can then dene in type v a So what we need here is a multiparameter stances of StateMonad class that overloads on v a and a instead newtype State s a State s as class IODevice handle a where instance StateMonad State s s where receive handle IO a send handle a IO a Notice the coupling b etween the parameters arising from the rep eated type variable s Jones also Perhaps one could go one step further and unify class denes a related class ReaderMonad that describ es IODevice r a and class Monad m StateMonad m computations that read some xed environment r into a three parameter class class Monad m Device m r a class Monad m ReaderMonad m e where env e m a m a 2 This example was suggested by Enno Scholz getenv m e 3 An app ealing application of type classes is to de serves as a ho ok either for one of the other arguments or scrib e mathematical structures such as groups elds for the instance context and member functions to use monoids and so on But it is not long b efore the need The parametric type classes of Chen Hudak Odersky for coupled overloading arises For example also deal quite nicely with the bulktypes example but their assymetry do es not suit the examples of the pre class Field k AdditiveGroup a vious section so well A full discussion of the design choices VectorSpace k a where for a bulktypes library is contained in Peyton Jones k a a Type relations Here the op erator multiplies a vector by a scalar One can also construct applications for multiparameter classes where the relationshi ps b etween dierent parame Overloading with constrained parame ters are much lo oser than in the examples that we have ters seen ab ove After all in the most general setting a multi parameter type class C could b e used to represent an arbi Libraries that implement sets bags lists nite maps and so trary relation b etween types where for example a b is in on all use similar functions empty
Recommended publications
  • System F and Existential Types 15-312: Foundations of Programming Languages
    Recitation 6: System F and Existential Types 15-312: Foundations of Programming Languages Serena Wang, Charles Yuan February 21, 2018 We saw how to use inductive and coinductive types last recitation. We can also add polymorphic types to our type system, which leads us to System F. With polymorphic types, we can have functions that work the same regardless of the types of some parts of the expression.1 1 System F Inductive and coinductive types expand the expressivity of T considerably. The power of type operators allows us to genericly manipulate data of heterogeneous types, building new types from old ones. But to write truly \generic" programs, we want truly polymorphic expressions| functions that operate on containers of some arbitrary type, for example. To gain this power, we add parametric polymorphism to the language, which results in System F, introduced by Girand (1972) and Reynolds (1974). Typ τ ::= t type variable τ1 ! τ2 function 8(t.τ) universal type Exp e ::= x variable λ (x : τ) e abstraction e1(e2) application Λ(t) e type abstraction e[τ] type application Take stock of what we've added since last time, and what we've removed. The familiar type variables are now baked into the language, along with the universal type. We also have a new form of lambda expression, one that works over type variables rather than expression variables. What's missing? Nearly every other construct we've come to know and love! As will be the case repeatedly in the course, our tools such as products, sums, and inductive types are subsumed by the new polymorphic types.
    [Show full text]
  • Recursive Type Generativity
    JFP 17 (4 & 5): 433–471, 2007. c 2007 Cambridge University Press 433 doi:10.1017/S0956796807006429 Printed in the United Kingdom Recursive type generativity DEREK DREYER Toyota Technological Institute, Chicago, IL 60637, USA (e-mail: [email protected]) Abstract Existential types provide a simple and elegant foundation for understanding generative abstract data types of the kind supported by the Standard ML module system. However, in attempting to extend ML with support for recursive modules, we have found that the traditional existential account of type generativity does not work well in the presence of mutually recursive module definitions. The key problem is that, in recursive modules, one may wish to define an abstract type in a context where a name for the type already exists, but the existential type mechanism does not allow one to do so. We propose a novel account of recursive type generativity that resolves this problem. The basic idea is to separate the act of generating a name for an abstract type from the act of defining its underlying representation. To define several abstract types recursively, one may first “forward-declare” them by generating their names, and then supply each one’s identity secretly within its own defining expression. Intuitively, this can be viewed as a kind of backpatching semantics for recursion at the level of types. Care must be taken to ensure that a type name is not defined more than once, and that cycles do not arise among “transparent” type definitions. In contrast to the usual continuation-passing interpretation of existential types in terms of universal types, our account of type generativity suggests a destination-passing interpretation.
    [Show full text]
  • Chapter 1 Basic Principles of Programming Languages
    Chapter 1 Basic Principles of Programming Languages Although there exist many programming languages, the differences among them are insignificant compared to the differences among natural languages. In this chapter, we discuss the common aspects shared among different programming languages. These aspects include: programming paradigms that define how computation is expressed; the main features of programming languages and their impact on the performance of programs written in the languages; a brief review of the history and development of programming languages; the lexical, syntactic, and semantic structures of programming languages, data and data types, program processing and preprocessing, and the life cycles of program development. At the end of the chapter, you should have learned: what programming paradigms are; an overview of different programming languages and the background knowledge of these languages; the structures of programming languages and how programming languages are defined at the syntactic level; data types, strong versus weak checking; the relationship between language features and their performances; the processing and preprocessing of programming languages, compilation versus interpretation, and different execution models of macros, procedures, and inline procedures; the steps used for program development: requirement, specification, design, implementation, testing, and the correctness proof of programs. The chapter is organized as follows. Section 1.1 introduces the programming paradigms, performance, features, and the development of programming languages. Section 1.2 outlines the structures and design issues of programming languages. Section 1.3 discusses the typing systems, including types of variables, type equivalence, type conversion, and type checking during the compilation. Section 1.4 presents the preprocessing and processing of programming languages, including macro processing, interpretation, and compilation.
    [Show full text]
  • Type Theory & Functional Programming
    Type Theory & Functional Programming Simon Thompson Computing Laboratory, University of Kent March 1999 c Simon Thompson, 1999 Not to be reproduced i ii To my parents Preface Constructive Type theory has been a topic of research interest to computer scientists, mathematicians, logicians and philosophers for a number of years. For computer scientists it provides a framework which brings together logic and programming languages in a most elegant and fertile way: program development and verification can proceed within a single system. Viewed in a different way, type theory is a functional programming language with some novel features, such as the totality of all its functions, its expressive type system allowing functions whose result type depends upon the value of its input, and sophisticated modules and abstract types whose interfaces can contain logical assertions as well as signature information. A third point of view emphasizes that programs (or functions) can be extracted from proofs in the logic. Up until now most of the material on type theory has only appeared in proceedings of conferences and in research papers, so it seems appropriate to try to set down the current state of development in a form accessible to interested final-year undergraduates, graduate students, research workers and teachers in computer science and related fields – hence this book. The book can be thought of as giving both a first and a second course in type theory. We begin with introductory material on logic and functional programming, and follow this by presenting the system of type theory itself, together with many examples. As well as this we go further, looking at the system from a mathematical perspective, thus elucidating a number of its important properties.
    [Show full text]
  • Type Systems, Type Inference, and Polymorphism
    6 Type Systems, Type Inference, and Polymorphism Programming involves a wide range of computational constructs, such as data struc- tures, functions, objects, communication channels, and threads of control. Because programming languages are designed to help programmers organize computational constructs and use them correctly, many programming languages organize data and computations into collections called types. In this chapter, we look at the reasons for using types in programming languages, methods for type checking, and some typing issues such as polymorphism and type equality. A large section of this chap- ter is devoted to type inference, the process of determining the types of expressions based on the known types of some symbols that appear in them. Type inference is a generalization of type checking, with many characteristics in common, and a representative example of the kind of algorithms that are used in compilers and programming environments to determine properties of programs. Type inference also provides an introduction to polymorphism, which allows a single expression to have many types. 6.1 TYPES IN PROGRAMMING In general, a type is a collection of computational entities that share some common property. Some examples of types are the type Int of integers, the type Int!Int of functions from integers to integers, and the Pascal subrange type [1 .. 100] of integers between 1 and 100. In concurrent ML there is the type Chan Int of communication channels carrying integer values and, in Java, a hierarchy of types of exceptions. There are three main uses of types in programming languages: naming and organizing concepts, making sure that bit sequences in computer memory are interpreted consistently, providing information to the compiler about data manipulated by the program.
    [Show full text]
  • Type Systems.Fm
    Type Systems Luca Cardelli Microsoft Research 1 Introduction The fundamental purpose of a type system is to prevent the occurrence of execution errors dur- ing the running of a program. This informal statement motivates the study of type systems, but requires clarification. Its accuracy depends, first of all, on the rather subtle issue of what consti- tutes an execution error, which we will discuss in detail. Even when that is settled, the absence of execution errors is a nontrivial property. When such a property holds for all of the program runs that can be expressed within a programming language, we say that the language is type sound. It turns out that a fair amount of careful analysis is required to avoid false and embar- rassing claims of type soundness for programming languages. As a consequence, the classifica- tion, description, and study of type systems has emerged as a formal discipline. The formalization of type systems requires the development of precise notations and defi- nitions, and the detailed proof of formal properties that give confidence in the appropriateness of the definitions. Sometimes the discipline becomes rather abstract. One should always remem- ber, though, that the basic motivation is pragmatic: the abstractions have arisen out of necessity and can usually be related directly to concrete intuitions. Moreover, formal techniques need not be applied in full in order to be useful and influential. A knowledge of the main principles of type systems can help in avoiding obvious and not so obvious pitfalls, and can inspire regularity and orthogonality in language design. When properly developed, type systems provide conceptual tools with which to judge the adequacy of important aspects of language definitions.
    [Show full text]
  • Abstract Data Types, Type Checking
    Programming Languages Third Edition Chapter 8 Data Types Objectives • Understand data types and type information • Understand simple types • Understand type constructors • Be able to distinguish type nomenclature in sample languages • Understand type equivalence Programming Languages, Third Edition 2 Objectives (cont’d.) • Understand type checking • Understand type conversion • Understand polymorphic type checking • Understand explicit polymorphism • Perform type checking in TinyAda Programming Languages, Third Edition 3 Introduction • Every program uses data, either explicitly or implicitly, to arrive at a result • Data type: the basic concept underlying the representation of data in programming languages • Data in its most primitive form is simply a collection of bits – This does not provide the kinds of abstraction necessary for large programs • Programming languages include a set of simple data entities and mechanisms for constructing new ones Programming Languages, Third Edition 4 Introduction (cont’d.) • Machine dependencies are often part of the implementation of these abstractions • Finitude of data: – In mathematics, integer set is infinite – In hardware, there is always a largest and smallest integer • Much disagreement among language designers on the extent to which type information should be made explicit and used to verify program correctness Programming Languages, Third Edition 5 Introduction (cont’d.) • Reasons to have some form of static type- checking: – Execution efficiency: allows compilers to allocate memory efficiently
    [Show full text]
  • Type Classes
    7 Type Classes A symbol is overloaded if it has two (or more) meanings, distinguished by type, that are resolved at compile time. For example, in Haskell, as in many other languages, the oper- ator + has (at least) two distinct implementations associated with it, one of type Int -> Int -> Int , the other of type Float -> Float -> Float . The reason that both of these operations are given the name + is that both compute numeric addition. However, at the implementation level, the two operations are really very different. Because integers are represented in one way (as binary numbers) and floating point numbers in another (as ex- ponent and mantissa, following scientific notation), the way that integer addition combines the bits of its arguments to produce the bits of its result is very different from the way this is done in floating point addition. A characteristic of overloading is that overloading is resolved at compile time. If a function is overloaded, then the compiler must choose between the possible algorithms at compile time. The process of choosing one algorithm from among the possible algorithms associated with an overloaded function is called overload resolution. In many languages, if a function is overloaded, then only the function arguments are used to resolve overloading. For example, consider the following two expressions: 3 + 2 f- add two integers - g 3.0 + 2.0 f- add two floating point numbers - g Here is how the compiler will produce code for evaluating each expression: ■ 3 + 2 : The parsing phase of the compiler will build the parse tree of this expres- sion, and the type-checking phase will compute a type for each symbol.
    [Show full text]
  • Type Variables in Patterns
    Type variables in patterns Richard A. Eisenberg Joachim Breitner Simon Peyton Jones Bryn Mawr College University of Pennsylvania Microsoft Research Bryn Mawr, PA, USA Philadelphia, PA, USA Cambridge, UK [email protected] [email protected] [email protected] Abstract need a way to name such types. The obvious way to address For many years, GHC has implemented an extension to this challenge is by providing language support for lexically Haskell that allows type variables to be bound in type sig- scoped type variables. GHC has long supported scoped type natures and patterns, and to scope over terms. This exten- variables: the ScopedTypeVariables extension is very popular, sion was never properly specified. We rectify that oversight and 29% of Haskell packages on Hackage use it. But it has here. With the formal specification in hand, the otherwise- never been formally specified! Moreover, as we shall see, it labyrinthine path toward a design for binding type vari- is in any case inadequate to the task. In this paper we fix ables in patterns becomes blindingly clear. We thus extend both problems, making the following contributions: ScopedTypeVariables to bind type variables explicitly, obvi- ating the Proxy workaround to the dustbin of history. In the days of Haskell98, scoped type variables were • 1 Introduction seldom crucial. Through a series of examples we show Haskell allows the programmer to write a type signature for a that, as Haskell’s type system has grown more sophis- definition or expression, both as machine-checked documen- ticated, the need for scoped type variables has become tation, and to resolve ambiguity (Section 2.1).
    [Show full text]
  • Session 6: Data Types and Representation
    Programming Languages Session 6 – Main Theme Data Types and Representation and Introduction to ML Dr. Jean-Claude Franchitti New York University Computer Science Department Courant Institute of Mathematical Sciences Adapted from course textbook resources Programming Language Pragmatics (3rd Edition) Michael L. Scott, Copyright © 2009 Elsevier 1 Agenda 11 SessionSession OverviewOverview 22 DataData TypesTypes andand RepresentationRepresentation 33 MLML 44 ConclusionConclusion 2 What is the course about? Course description and syllabus: » http://www.nyu.edu/classes/jcf/g22.2110-001 » http://www.cs.nyu.edu/courses/fall10/G22.2110-001/index.html Textbook: » Programming Language Pragmatics (3rd Edition) Michael L. Scott Morgan Kaufmann ISBN-10: 0-12374-514-4, ISBN-13: 978-0-12374-514-4, (04/06/09) Additional References: » Osinski, Lecture notes, Summer 2010 » Grimm, Lecture notes, Spring 2010 » Gottlieb, Lecture notes, Fall 2009 » Barrett, Lecture notes, Fall 2008 3 Session Agenda Session Overview Data Types and Representation ML Overview Conclusion 4 Icons / Metaphors Information Common Realization Knowledge/Competency Pattern Governance Alignment Solution Approach 55 Session 5 Review Historical Origins Lambda Calculus Functional Programming Concepts A Review/Overview of Scheme Evaluation Order Revisited High-Order Functions Functional Programming in Perspective Conclusions 6 Agenda 11 SessionSession OverviewOverview 22 DataData TypesTypes andand RepresentationRepresentation 33 MLML 44 ConclusionConclusion 7 Data Types and Representation
    [Show full text]
  • Type Classes in Haskell
    Type classes in Haskell Cordelia Hall, Kevin Hammond, Simon Peyton Jones and Philip Wadler Glasgow University Abstract This paper defines a set of type inference rules for resolving over- loading introduced by type classes. Programs including type classes are transformed into ones which may be typed by the Hindley- Milner inference rules. In contrast to other work on type classes, the rules presented here relate directly to user programs. An innovative aspect of this work is the use of second-order lambda calculus to record type information in the program. 1. Introduction The goal of the Haskell committee was to design a standard lazy functionM language, applying existing, well-understood methods. To the committee's surprise, it emerged that there was no standard way to provide overloaded operations such as equality (==), arithmetic (+), and conversion to a string Languages such as Mirandal[Tur85] and Standard ML [MTH90, MT91] offer differing solutions to these problems. The solutions differ not only between languages, but within a language. Miranda uses one technique for equality (it is defined on all types - including abstract types on which it should be undefined!), another for arithmetic (there is only one numeric type), and a third for string conversion. Standard ML uses the same technique for arithmetic and string conversion (overloading must be resolved at the point of appearance), but a different one for equality (type variables that range only over equality types). The committee adopted a completely new technique, based on a proposal by Wadler, which extends the familiar Hindley-Milner system [Mil78] with type classes. Type classes provide a uniform solution to overloading, including providing operations for equality, arithmetic, and string conversion.
    [Show full text]
  • Type Systems, Type Inference, and Polymorphism
    6 Type Systems, Type Inference, and Polymorphism Programming involves a wide range of computational constructs, such as data structures, functions, objects, communication channels, and threads of control. Because programming languages are designed to help programmers organize computational constructs and use them correctly, many programming languages organize data and computations into collec- tions called types. In this chapter, we look at the reasons for using types in programming languages, methods for type checking, and some typing issues such as polymorphism and type equality. A large section of this chapter is devoted to type inference, the process of de- termining the types of expressions based on the known types of some symbols that appear in them. Type inference is a generalization of type checking, with many characteristics in common, and a representative example of the kind of algorithms that are used in compilers and programming environments to determine properties of programs. Type inference also provides an introduction to polymorphism, which allows a single expression to have many types. 6.1 TYPES IN PROGRAMMING In general, a type is a collection of computational entities that share some common prop- erty. Some examples of types are the type Int of integers, the type Int !Int of functions from integers to integers, and the Pascal subrange type [1 .. 100] of integers between 1 and 100. In concurrent ML there is the type Chan Int of communication channels carrying integer values and, in Java, a hierarchy of types of exceptions. There are three main uses of types in programming languages: ■ naming and organizing concepts, ■ making sure that bit sequences in computer memory are interpreted consistently, ■ providing information to the compiler about data manipulated by the program.
    [Show full text]