Introduction to Gofer 14. OVERLOADING IN GOFER 14. OVERLOADING IN GOFER One of the biggest differences between Gofer and most other programming languages (with the exception of Haskell) is the approach to overloading; enabling the definition and use of functions in which the meaning of a function symbol may depend on the types of its arguments. Like Haskell, overloading in Gofer is based around a system of type classes which allow overloaded functions to be grouped together into related groups of functions. Whilst the precise details of the approach to type classes used by Gofer are quite different from those of Haskell, both rely on the same basic ideas and use a similar syntax for defining and using type classes. It would therefore seem possible that experience gained with the overloading system in one language can readily by applied to the other. The differences embodied in the Gofer system of classes stem from my own, theoretically based investigations into `qualified types' some of which is detailed in references [8-12]. In my personal opinion, the Gofer system has some significant advantages over the Haskell approach (see [12] for details) and one of the principal motivations behind the implementation to Gofer was to provide a way of testing such claims. One fact which I believe has already been established using Gofer is that the use and implementation of overloaded functions need not have the significant effect on performance that was anticipated with early implementations of Haskell. This section outlines the system of type classes used in Gofer, indicating briefly how they can be used and how they are implemented. 14.1 Type classes and predicates -------------------------------- A type class can be thought of as a family of types (or more generally as a family of tuples of types) whose elements are called instances of the class. If C is the name of an n-parameter type class then an expression of the form C t1 t2 ... tn where t1, t2, ..., tn are type expressions is called a predicate and represents the assertion that the specified tuple of types is an instance of the class C. Given a polymorphic function (e.g. map::(a->b)->[a]->[b]), we are free to use the function at any type which can be obtained by substituting arbitrary types for each of the type variables in its type. In Gofer, a type expression may be qualified by one or more predicates which restrict the range of types at which a value can be used: e.g. a function of type C a => a -> a -> a can be treated as a function of type t -> t -> t for any instance t of the class C. The predicate C a in the type expression in the previous example is called the context of the type. Contexts may contain more than one predicate in which case the predicates involved must be separated by commas and the context enclosed in parentheses as in (C a, D b). The empty context is written () and any type expression t is equivalent to the qualified type () => t. For uniformity, a context with only one element may also be enclosed by parentheses. 61 Introduction to Gofer 14.1 Type classes and predicates For technical reasons, type synonyms are not currently permitted in predicates. This is consistent with the use of predicates in Haskell, but may be relaxed, at least in certain cases, in later versions of Gofer. 14.2 The type class Eq ---------------------- The type class Eq is a simple and useful example, whose instances are precisely those types whose elements can be tested for equality. The declaration of this class given in the standard prelude is as follows: class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) There are three parts in any class declaration. For this particular example we have: o The first line (called the `header') of the declaration introduces a name Eq for the class and indicates that it has a single parameter, represented by the type variable a. o The second line of the declaration (the `signature part') indicates that there are functions denoted by the operator symbols (==) and (/=) of type a -> a -> Bool for each instance a of class Eq. Using the notation introduced in the previous section, both of these operators have type: Eq a => a -> a -> Bool These functions are called the `members' (or `member functions') of the class. [This terminology, taken from Haskell, is rather unfortunate; thinking of a type class as a set of types, the elements of the class are called `instances', whilst the `members' of the class correspond more closely to the instance variables that are used in the terminology of object-oriented programming.] The intention is that the (==) function will be used to implement an equality test for each instance of the class, with the (/=) operator providing the corresponding inequality function. The ability to include related groups of functions within a single type class in this way is a useful tool in program design. o The third line of the class declaration (the `default definitions') provides a default definition of the (/=) operator in terms of the (==) operator. Thus it is only necessary to give a definition for the (==) operator in order to define all of the member functions for the class Eq. It is possible to override default member definitions by giving an alternative definition as appropriate for specific instances of the class. 62 Introduction to Gofer 14.2.1 Implicit overloading 14.2.1 Implicit overloading --------------------------- Member functions are clearly marked as overloaded functions by their definition as part of a class declaration, but this is not the only way in which overloaded functions occur in Gofer; the restriction to particular instances of a type class is also carried over into the type of any function defined either directly or indirectly in terms of the member functions of that class. For example, the types inferred for the following two functions: x `elem` xs = any (x==) xs xs `subset` ys = all (`elem` ys) xs are: elem :: Eq a => a -> [a] -> Bool subset :: Eq a => [a] -> [a] -> Bool [ASIDE: On the other hand, if none of the functions used in a particular expression or definition are overloaded then there will not be any overloading in the corresponding value. Gofer does not support the concept of implicit overloading used in some languages where a value of a particular type might automatically be coerced to a value of some supertype. An example of this would be the automatic translation of a badly typed expression "1.0 == 1" to a well-typed expression of the form "1.0 == float 1" for some (potentially overloaded) coercion function "float" mapping numeric values to elements of type Float.] Note also that the types appearing in the context of a qualified type reflect the types at which overloaded functions are used. Thus: f x ys = [x] == ys has type Eq [a] => a -> [a] -> Bool, and not Eq a => a -> [a] -> Bool, which is the type that would be assigned to "f" in a Haskell system. 14.2.2 Instances of class Eq ---------------------------- Instances of a type class are defined using declarations similar to those used to define the corresponding type class. The following examples, taken from the standard prelude, give the definitions for a number of simple instances of the class Eq: instance Eq Int where (==) = primEqInt instance Eq Bool where True == True = True False == False = True _ == _ = False instance Eq Char where c == d = ord c == ord d instance (Eq a, Eq b) => Eq (a,b) where (x,y) == (u,v) = x==u && y==v 63 Introduction to Gofer 14.2.2 Instances of class Eq instance Eq a => Eq [a] where [] == [] = True [] == (y:ys) = False (x:xs) == [] = False (x:xs) == (y:ys) = x==y && xs==ys The interpretation of these declarations is as follows: o The first declaration makes Int an instance of class Eq. The function "primEqInt" is a primitive Gofer function which tests the equality of two integer values and has type Int -> Int -> Bool. o The second declaration makes Bool an instance of class Eq with a simple definition involving pattern matching. o The third declaration makes Char an instance of class Eq. This definition indicates that a pair of characters are equal if they have the same ASCII value, which is obtained using the "ord" function. Note that the two occurrences of the symbol (==) in the equation: c == d = ord c == ord d have different meanings; the first denotes equality between characters (elements of type Char), whilst the second denotes equality between integers (elements of type Int). o The fourth declaration provides an equality operation on pairs. Given two elements (x,y) and (u,v) of type (a,b) for some a, b, it must be possible to check that both x==u and y==v before we can be sure that the two pairs are indeed equal. In other words, both a and b must also be instances of Eq in order to make (a,b) an instance of Eq. This requirement is described by the first line in the instance declaration using the expression: (Eq a, Eq b) => Eq (a,b) o The fifth declaration makes [a] an instance of Eq, whenever a is itself an instance of Eq in a similar way to the previous example. The context Eq a is used in the last equation in the declaration: (x:xs) == (y:ys) = x==y && xs==ys which contains three occurrences of the (==) operator; the first and third are used to compare lists of type [a], whilst the second is used to compare elements of type a, using the instance Eq a. Combining these five declarations, we obtain definitions for (==) on an infinite family of types including Int, Char, Bool, (Int,Bool), (Char,Int), [Char], (Bool,[Int]), [(Bool,Int)], etc...: ? 2 == 3 -- using Eq Int False (2 reductions, 10 cells) ? (["Hello"],3) == (["Hello"],3) -- using Eq ([[Char]],Int) 64 Introduction to Gofer 14.2.2 Instances of class Eq True (31 reductions, 65 cells) ? On the other hand, any attempt to use (==) to compare elements of some type not covered by a suitable instance declaration will result in an error. For example, the standard prelude does not define the equality operation on triples of values: ? (1,2,3) == (1,2,3) ERROR: Cannot derive instance in expression *** Expression : (==) d125 (1,2,3) (1,2,3) *** Required instance : Eq (Int,Int,Int) ? This can be solved by including an instance declaration of the following form into a file of definitions loaded into Gofer: instance (Eq a, Eq b, Eq c) => Eq (a,b,c) where (x,y,z) == (u,v,w) = x==u && y==v && z==w Giving: ? (1,2,3) == (1,2,3) True (6 reductions, 20 cells) ? In general, an instance declaration has the form: instance context => predicate where definitions of member functions The context part of the declaration gives a list of predicates which must be satisfied for the predicate on the right hand side of the `=>' sign to be valid. Constant predicates (i.e. predicates not involving any type variables) required by an instance declaration (such as the predicate Eq Int required by the third declaration) need not be included in the context. If the resulting context is empty (as in the first three declarations above) then it may be omitted, together with the corresponding `=>' symbol. 14.2.3 Testing equality of represented values --------------------------------------------- Instances of Eq can also be defined for other types, including user-defined datatypes, and unlike the instances described above, the definition of (==) need not be used to determine whether the values being compared have the same structure; it is often more useful to check that they represent the same value. As an example, suppose that we introduce a type constructor Set for representing sets of values, using a list to store the values held in the set: data Set a = Set [a] As usual, we say that two sets are equal if they have the same members, 65 Introduction to Gofer 14.2.3 Testing equality of represented values ignoring any repetitions or differences in the ordering of the elements in the lists representing the sets. This is achieved using the following instance declaration: instance Eq a => Eq (Set a) where Set xs == Set ys = xs `subset` ys && ys `subset` xs where xs `subset` ys = all (`elem` ys) xs A couple of examples illustrate the use of this definition: ? Set [1,2,3] == Set [3,4,1] False (49 reductions, 89 cells) ? Set [1,2,3] == Set [1,2,2,2,1,3] True (157 reductions, 240 cells) ? 14.2.4 Instance declarations without members -------------------------------------------- It is possible to give an instance declaration without specifying any definitions for the member functions of the class. For example: instance Eq () In this case, the definition of (==) for the instance Eq () is left completely undefined, and hence so is the definition of (/=), which is defined in terms of (==): ? () == () {undefined_member (==)} (3 reductions, 34 cells) ? () /= () {undefined_member (==)} (4 reductions, 36 cells) ? 14.2.5 Equality on function types --------------------------------- If an expression requires an instance of a class which cannot be obtained using the rules in the given instance declarations, then an error message will be produced when the expression is type-checked. For example, in general there is no sensible way to determine when a pair of functions are equal, and the standard prelude does not include a definition for an instance of the form Eq (a -> b) for any types a and b: ? (1==) == (\x->1==x) ERROR: Cannot derive instance in expression *** Expression : (==) d148 ((==) {dict} 1) (\x->(==) {dict} 1 x) *** Required instance : Eq (Int -> Bool) ? 66 Introduction to Gofer 14.2.5 Equality on function types If for some reason, you would prefer this kind of error to produce an error message when an expression is evaluated, rather than when it is type-checked, you can use an instance declaration to specify the required behaviour. For example: instance Eq (a -> b) where (==) = error "Equality not defined between functions" Evaluating the previous expression once this instance declaration has been included now produces the following result: ? (1==) == (\x->1==x) {error "Equality not defined between functions"} (42 reductions, 173 cells) ? A limited form of equality can be defined for functions of type (a->b) if a has only finitely many elements, such as the boolean type Bool: instance Eq a => Eq (Bool -> a) where f == g = f False == g False && f True == g True [ASIDE: This instance declaration would not be accepted in a Haskell program which insists that the predicate on the right of the `=>' symbol contains precisely one type constructor symbol.] Using this instance declaration once for each argument, we can now test two functions taking boolean arguments for equality (assuming of course that their result type is also an instance of Eq). ? (&&) == (||) False (9 reductions, 21 cells) ? not == (\x -> if x then False else True) True (8 reductions, 16 cells) ? (&&) == (\x y-> if x then y else False) True (16 reductions, 30 cells) ? 14.2.6 Non-overlapping instances -------------------------------- Other instance declarations for types of the form a -> b can be used at the same time, so long as no pair of declarations overlap. For example, adding the following instance declaration instance Eq a => Eq (() -> a) where f == g = f () == g () enables us to evaluate expressions such as: ? (\()->"Hello") == const "Hello" True (30 reductions, 55 cells) ? 67 Introduction to Gofer 14.2.6 Non-overlapping instances If however, we try to use instance declarations for types of the form (a -> b) and (Bool -> a) at the same time, then Gofer produces an error message similar to the following: ERROR "file" (line 37): Overlapping instances for class "Eq" *** This instance : Eq (a -> b) *** Overlaps with : Eq (Bool -> a) *** Common instance : Eq (Bool -> a) ? indicating that, given the task of testing two values of type (Bool->a) for equality, there are (at least) two definitions of (==) that could be used, with potentially different results being obtained in each case. Here is a further example of the use of non-overlapping instances of a class to define a function "cat" (inspired by the unix (tm) command of the same name) which uses the I/O facilities of Gofer to print the contents of one or more files on the terminal: class Cat a where cat :: a -> Dialogue instance Cat [Char] where cat n = showFile n done instance Cat [[Char]] where cat = foldr showFile done showFile name cont = readFile name abort (\s->appendChan stdout s abort cont) Given these declarations, an expression of the form: cat "file" can be used to display the contents of the named file, whilst a list of files can be printed one after the other using an expression of the form: cat ["file1", "file2", ..., "filen"]. 14.3 Dictionaries ----------------- In order to understand some of the messages produced by Gofer, as well as some of the more subtle problems associated with overloaded functions, it is useful to have a rough idea of the way in which overloaded functions are implemented. The basic idea is that a function with a qualified type context => type where context is a non-empty list of predicates is implemented by a function which takes an extra argument for each predicate in the context. When the function is used, each of these parameters is filled by a `dictionary' which gives the values of each of the member functions in the appropriate class. None of these extra parameters is entered by the programmer. Instead, they are inserted automatically during type-checking. For the class Eq, each dictionary has at least two elements containing 68 Introduction to Gofer 14.3 Dictionaries definitions for each of the functions (==) and (/=). A dictionary for an instance of Eq can be depicted by a diagram of the form: +--------+--------+--------- | | | | (==) | (/=) | ..... | | | +--------+--------+--------- In order to produce useful error messages and indicate the way in which dictionary expressions are being used, Gofer uses a number of special notations for printing expressions involving dictionaries: (#1 d) selects the first element of the dictionary d (#2 d) selects the second element of the dictionary d (#n d) selects the nth element of the dictionary d (note that (#0 d) is equivalent to the dictionary d). {dict} denotes a specific dictionary (the contents are not displayed). dnnn a dictionary variable representing an unknown dictionary is printed as a lower case letter `d' followed by an integer; e.g. d231. Note that, whilst these notations are used in output produced by Gofer and in the following explanations, they cannot be entered directly into Gofer expressions or programs -- even if you use a variable such as "d1" in an expression, Gofer will not confuse this with a dictionary variable with the same name (although Gofer might confuse you by using the same name in two different contexts!). Using these notations, the member functions (==) and (/=) of the class Eq behave as if they were defined by the expressions: (==) d1 = (#1 d1) (/=) d1 = (#2 d1) To understand how these definitions work, we need to take a look at a specific dictionary. Following the original instance declaration used for Eq Int, the corresponding dictionary is: d :: Eq Int +------------+------------+ | | | | primEqInt | defNeq d | | | | +------------+------------+ Note that the dictionary variable d is used as a name for the dictionary in this diagram, indicating how values within a dictionary can include references to the same dictionary. [ASIDE: It turns out that predicates play a very similar role for 69 Introduction to Gofer 14.3 Dictionaries dictionaries as types play for normal values. This motivates our use of the notation d :: Eq Int to indicate that d is a dictionary for the instance Eq Int. One difference between these, particularly important for theoretical work, is that dictionary values are uniquely determined by predicates; if d1::p and d2::p for some predicate p, then d1 = d2.] The value held in the first element of the dictionary is the primitive equality function on integers, "primEqInt". The following diagram shows how the dictionary is used to evaluate the expression "2 == 3". Note that this expression will first be translated to "(==) d 2 3" by the type checker. The evaluation then proceeds as follows: (==) d 2 3 ==> (#1 d) 2 3 ==> primEqInt 2 3 ==> False The second element of the dictionary is a little more interesting because it uses the default definition for (/=) given in the original class definition which, after translation, is represented by the function "defNeq" defined by: defNeq d1 x y = not ((==) d1 x y) Notice the way in which the extra dictionary parameter is used to obtain the appropriate overloading. For example, evaluation of the expression "2 /= 3", which becomes "(/=) d 2 3" after translation, proceeds as follows: (/=) d 2 3 ==> (#2 d) 2 3 ==> defNeq d 2 3 ==> not ((==) d 2 3) ==> not ((#1 d) 2 3) ==> not (primEqInt 2 3) ==> not False ==> True [Clearly there is some scope for optimisation here; whilst the actual reduction sequences used by Gofer are equivalent to those illustrated above, the precise details are a little different.] If an instance is obtained from an instance declaration with a non-empty context, then the basic two element dictionary used in the examples above is extended with an extra dictionary value for each predicate in the context. As an example, the diagram below shows the dictionaries that will be created from the instance definitions in section 14.2.2 for the instance Eq (Int, [Int]). The functions "eqPair" and "eqList" which are used in these dictionaries are obtained from the definitions of (==) given in the instance declarations for Eq (a,b) and Eq [a] respectively: eqPair d (x,y) (u,v) = (==) (#3 d) x u && (==) (#4 d) y v eqList d [] [] = True eqList d [] (y:ys) = False eqList d (x:xs) [] = False eqList d (x:xs) (y:ys) = (==) (#3 d) x y && (==) d xs ys 70 Introduction to Gofer 14.3 Dictionaries The dictionary structure for Eq (Int, [Int]) is as follows. Note that the Gofer system ensures that there is at most one dictionary for a particular instance of a class, and that the dictionary d1 :: Eq Int in this system is automatically shared between d2 and d3: d3 :: Eq (Int, [Int]) +------------+------------+------------+------------+ | | | | | | eqPair d3 | defNeq d3 | d1::Eq Int |d2::Eq [Int]| | | | | | +------------+------------+-----+------+-----+------+ | | +--------------+ | | | | d2 :: Eq [Int] V | +------------+------------+------------+ | | | | | | | eqList d2 | defNeq d2 | d1::Eq Int | | | | | | | +------------+------------+-----+------+ | | d1 :: Eq Int V | +------------+------------+ | | | | | | primEqInt | defNeq d1 |<--------------------------+ | | | +------------+------------+ Once again, it may be useful to see how these definitions are used to evaluate the expression "(2,[1]) == (2,[1,3])" which, after translation, becomes "(==) d3 (2,[1]) (2,[1,3])": (==) d3 (2,[1]) (2,[1,3]) ==> (#1 d3) (2,[1]) (2,[1,3]) ==> eqPair d3 (2,[1]) (2,[1,3]) ==> (==) (#3 d3) 2 2 && (==) (#4 d3) [1] [1,3] ==> (==) d1 2 2 && (==) (#4 d3) [1] [1,3] ==> (#1 d1) 2 2 && (==) (#4 d3) [1] [1,3] ==> primEqInt 2 2 && (==) (#4 d3) [1] [1,3] ==> True && (==) (#4 d3) [1] [1,3] ==> (==) (#4 d3) [1] [1,3] ==> (==) d2 [1] [1,3] ==> (#1 d2) [1] [1,3] ==> eqList d2 [1] [1,3] ==> (==) (#3 d2) 1 1 && (==) d2 [] [3] ==> (==) d1 1 1 && (==) d2 [] [3] ==> (#1 d1) 1 1 && (==) d2 [] [3] ==> primEqInt 1 1 && (==) d2 [] [3] ==> True && (==) d2 [] [3] ==> (==) d2 [] [3] ==> False 71 Introduction to Gofer 14.3.1 Superclasses 14.3.1 Superclasses ------------------- In general, a type class declaration has the form: class context => Class a1 ... an where type declarations for member functions default definitions of member functions where Class is the name of the new type class which takes n arguments, represented by distinct type variables a1, ..., an. As in the case of instance declarations, the context that appears on the left hand side of the `=>' symbol specifies a list of predicates that must be satisfied in order to construct any instance of "Class". The predicates in the context part of a class declaration are called the superclasses of Class. This terminology is taken from Haskell where all classes have a single parameter and each of the predicates in the context part of a class declaration has the form C a1; in this situation, any instance of Class must also be an instance of each class C named in the context. In other words, each such C contains a superset of the types in Class. As an example of a class declaration with a non-empty context, consider the following declaration from the standard prelude which introduces a class Ord whose instances are types with both strict (<), (>) and non-strict (<=), (>=) versions of an ordering defined on their elements: class Eq a => Ord a where (<), (<=), (>), (>=) :: a -> a -> Bool max, min :: a -> a -> a x < y = x <= y && x /= y x >= y = y <= x x > y = y < x max x y | x >= y = x | y >= x = y min x y | x <= y = x | y <= x = y Notice that this definition provides default definitions for all of the member functions except (<=), so that in general only this single function needs to be defined to construct an instance of class Ord. There are two reasons for defining Eq as a superclass of Ord: o The default definition for (<) relies on the use of (/=) taken from class Eq. In order to guarantee that this is always valid we must ensure that every instance of Ord must also be an instance of Eq. o Given the definition of a non-strict ordering (<=) on the elements of a type, it is always possible to construct a definition for the (==) operator (and hence for (/=)) using the equation: 72 Introduction to Gofer 14.3.1 Superclasses x==y = x<=y && y<=x There will therefore be no loss in generality by requiring Eq to be a superclass of Ord, and conversely, no difficulty in defining an instance of Eq to accompany any instance of Ord for which an instance of Eq has not already be provided. As an example, the following definitions provide an alternative way to implement the equality operation on elements of the Set datatype described in section 14.2.3, in terms of the subset ordering defined in class Ord: instance Ord (Set a) => Eq (Set a) where x == y = x <= y && y <= x instance Eq a => Ord (Set a) where Set xs <= Set ys = all (`elem` ys) xs This definition is in fact no less efficient or effective than the original version. Dictionaries for superclasses are dealt with in much the same way as the instance specific dictionaries described above. For example, the general layout of a dictionary for an instance of Ord is illustrated in the following diagram: +--------+--------+--------+--------+--------+--------+--------+----- | | | | | | | | | (<) | (<=) | (>) | (>=) | max | min | Eq a | ..... | | | | | | | | +--------+--------+--------+--------+--------+--------+--------+----- Note the use of the seventh element of this dictionary which points to the dictionary for the appropriate instance of Eq. This is used in the translation of the default definition for (<) which is equivalent to: defLessThan d x y = (<=) d x y && (/=) (#7 d) x y 14.3.2 Combining classes ------------------------ In general, a dictionary is made up of three separate parts: +-------------------+-------------------+-------------------+ | Implementation | Superclass | Instance specific | | of class members | Dictionaries | Dictionaries | | | | | +-------------------+-------------------+-------------------+ Each of these may be empty. We have already seen examples in which there are no superclass dictionaries (e.g. instances of Eq) and in which there are no instance specific dictionaries (e.g. Eq Int). Classes with no member functions (corresponding to dictionaries with no member functions) are sometimes useful as a convenient abbreviation for a list of predicates. For example: 73 Introduction to Gofer 14.3.2 Combining classes class C a where cee :: a -> a class D a where dee :: a -> a class (C a, D a) => CandD a makes CandD a an abbreviation for the context (C a, D a). Thinking of single parameter type classes as sets of types, the type class CandD corresponds to the intersection of classes C and D. Just as the type inferred for a particular function definition or expression does not involve type synonyms unless explicit type signatures are used, the Gofer type system will not use a single predicate of the form CandD a instead of the two predicates C a and D a unless explicit signatures are used: ? :t dee . cee \d129 d130 -> dee d130 . cee d129 :: (C a, D a) => a -> a ? :t dee . cee :: CandD a => a -> a \d129 -> dee (#2 d129) . cee (#1 d129) :: CandD a => a -> a ? In Haskell, all instances of a class such as CandD must have explicit declarations, in addition to the corresponding declarations for instances for C and D. This problem can be avoided by using the more general form of instance declaration permitted in Gofer; a single instance declaration: instance CandD a is all that is required to ensure that any instance of CandD can be obtained, so long as corresponding instances for C and D can be found. 14.3.3 Simplified contexts -------------------------- Consider the function defined by the following equation: eg1 x = [x] == [x] || x == x This definition does not restrict the type of x in any way except that, if x :: a, then there must be instances Eq [a] and Eq a which are used for the two occurrences of the (==) operator in the equation. We might therefore expect the type of eg1 to be: (Eq [a], Eq a) => a -> Bool with translation: eg1 d1 d2 x = (==) d1 [x] [x] || (==) d2 x x However, as can be seen from the case where a=Int illustrated in section 14.3, given d1::Eq [a] we can always find a dictionary for Eq a by taking the third element of d1 i.e. (#3 d1)::Eq a. Since it is more efficient to select an element from a dictionary than to complicate both type and translation with extra parameters, the type assigned to "eg1" by default is: 74 Introduction to Gofer 14.3.3 Simplified contexts Eq [a] => a -> Bool with translation: eg1 d1 x = (==) d1 [x] [x] || (==) (#3 d1) x x In general, given a set of predicates corresponding to the instances required by an expression, Gofer will always attempt to find the smallest possible subset of these predicates such that all of the required dictionaries can still be obtained, whilst minimising the number of dictionary parameters that are used. The original type and translation for eg1 given above can be produced by including an explicit type signature in the file containing the definition of eg1: eg1 :: (Eq [a], Eq a) => a -> Bool eg1 x = [x] == [x] || x == x But even with this definition, Gofer will still always try to minimise the number of dictionaries used in any particular expression: ? :t eg1 \d153 -> eg1 d153 (#3 d153) :: Eq [a] => a -> Bool ? As another example, consider the expression "(\x y-> x==x || y==y)". The type and translation assigned to this term can be found directly using Gofer: ? :t (\x y-> x==x || y==y) \d121 d122 x y -> (==) d122 x x || (==) d121 y y :: (Eq b, Eq a) => a -> b -> Bool ? Note that the translation has two dictionary parameters d121 and d122 corresponding to the two predicates Eq a and Eq b respectively. Since both of these dictionaries can be obtained from a dictionary for the predicate Eq (a,b), we can use an explicit type signature to produce a translation which needs only one dictionary parameter: ? :t (\x y-> x==x || y==y) :: Eq (a,b) => a -> b -> Bool \d121 x y -> (==) (#3 d121) x x || (==) (#4 d121) y y :: Eq (a,b) => a -> b -> Bool ? 75 Introduction to Gofer 14.4 Other issues 14.4 Other issues ----------------- 14.4.1 Unresolved overloading ----------------------------- Consider the use of the (==) operator in the following three situations: o In the expression "2 == 3", it is clear that the appropriate value for the equality operator in this case is primIntEq as defined by the instance declaration for Eq Int. The expression can therefore be translated to "primEqInt 2 3". o In the function definition "f x = x==x", we cannot completely determine the appropriate value for (==) because it depends on the type assigned to the variable "x", which may itself vary with different uses of the function "f". It is however possible to add an extra parameter to the definition, giving "f d x = (==) d x x" and taking the type of "f" to be Eq a => a -> Bool. In this way, the problem of finding the appropriate definition for the (==) operator is deferred until the function is actually used. o In the expression "[]==[]", the appropriate value for (==) must be obtained from the dictionary for some instance of the form Eq [a], but there is not sufficient information in the expression to determine what the value of the type variable a should be. Looking back to the instance declaration for Eq [a], we find that the definition of (==) depends on the value of the dictionary for the instance Eq a. In this particular case, it is clear that the expression will always evaluate to True, regardless of the value of this dictionary. Unfortunately, the only way that this can be detected is by evaluating the expression to see if the calculation can be completed without reference to the dictionary value (see the comments in the aside at the end of this section). Attempting to evaluate this expression in Gofer will therefore result in an error message indicating that the expression does not contain sufficient information to resolve the use of overloading in the expression: ? [] == [] ERROR: Unresolved overloading *** type : Eq [a] => Bool *** translation : \d129 -> (==) d129 [] [] ? Note that the expression has been converted into a lambda expression using the dictionary variable d129 to represent the dictionary for the unknown instance Eq [a]. One simple way to resolve the overloading in an expression of this kind is to use an explicit type signature. For example, if we specify that the second empty list is an empty list of type [Int]: 76 Introduction to Gofer 14.4.1 Unresolved overloading ? [] == ([]::[Int]) True (2 reductions, 9 cells) ? The same problem occurs in Haskell, where it is described using the idea of an `ambiguous type' -- i.e. a type expression of the form context => type where one or more of the type variables appearing in the given context do not appear in the remaining part of the type expression. Further examples of unresolved overloading occur with other classes. As an example consider the class Reader defined by: class Reader a where parse :: String -> a unparse :: a -> String whose member functions provide methods for obtaining the string representation of an element of an instance type, and for converting such representations back into the original values. (The standard Haskell Text class contains similar functions.) Now consider the expression "parse . unparse" which maps values from some instance of Reader to values of another instance via an intermediate string representation. ? parse . unparse ERROR: Unresolved overloading *** type : (Reader a, Reader b) => a -> b *** translation : \d129 d130 -> parse d130 . unparse d129 ? One of the first things that might surprise the reader here is that the value produced by "parse . unparse" does not have to be of the same type as the argument; for example, we would not usually expect to have any sensible interpretation for a floating point number obtained from the string representation of a boolean value! This can be fixed by using an explicit type declaration, although the expression still produces unresolved overloading: ? (parse . unparse) :: Reader a => a -> a ERROR: Unresolved overloading *** type : Reader a => a -> a *** translation : \d130 -> parse d130 . unparse d130 ? Notice however that the type of this expression is not ambiguous so that the unresolved overloading in this example can be eliminated when the function is actually used: ? ((parse . unparse) :: Reader a => a -> a) 'a' 'a' (4 reductions, 11 cells) ? 77 Introduction to Gofer 14.4.1 Unresolved overloading A more serious problem occurs with the expression "unparse . parse" which maps string values to string values via some intermediate type. Clearly this will lead to a problem with unresolved overloading: ? unparse . parse ERROR: Unresolved overloading *** type : Reader a => String -> String *** translation : \d130 -> unparse d130 . parse (#0 d130) ? Notice that the type obtained in this case is ambiguous; the type variable a which appears in the predicate Reader a does not appear in the type String -> String. There are a number of ways of resolving this kind of ambiguity: o Using an explicitly typed expression: Assuming for example that Char is an instance of Reader, we can write: ? unparse . (parse :: String -> Char) v113 {dict} . v112 {dict} (5 reductions, 42 cells) ? without any ambiguity. If such type signatures are used in a number of places, it might be better to define an auxiliary function and use that instead: charParse :: String -> Char charParse = parse ? unparse . charParse v113 {dict} . charParse (4 reductions, 37 cells) ? In such situations, it is perhaps worth asking if overloaded functions are in fact the most appropriate solution for the problem at hand! o Using an extra dummy parameter in a function definition. In a definition such as: f = unparse . parse we can introduce an additional dummy parameter `x' which is not used except to determine the type of the result produced by parse in f: f x = unparse . (parse `asTypeOf` (\""->x)) where the standard prelude operator `asTypeOf` defined by: asTypeOf :: a -> a -> a x `asTypeOf` _ = x is used to ensure that the type of parse in the definition of f is 78 Introduction to Gofer 14.4.1 Unresolved overloading the same as that of the function (\""->x) -- in other words, the type must be String -> a where a is the type of the variable x. The resulting type for f is: f :: Reader a => a -> String -> String Notice how the addition of the dummy parameter has been used to eliminate the ambiguity present in the original type. This kind of `coding trick' is rather messy and is not recommended for anything but the simplest examples. [ASIDE: The idea of evaluating an expression with an ambiguous type to see if it does actually need the unspecified dictionaries could have been implemented quite easily in Gofer using an otherwise unused datatype Unresolved and generating instance declarations such as: instance Eq Unresolved where (==) = error "unresolved overloading for (==)" (/=) = error "unresolved overloading for (/=)" for each class. Given a particular expression, we can then use the type Unused in place of any ambiguous type variables in its type. The evaluation of the expression could then be attempted, either completing successfully if the dictionaries are not required, but otherwise resulting in a run-time error. This approach is not used in Gofer; instead, the programmer is notified of any unresolved polymorphism when the program is type checked, avoiding the possibility that a program might contain an undetected ambiguity.] 14.4.2 `Recursive' dictionaries ------------------------------- Unlike Haskell, there are no restrictions on the form of the predicates that may appear in the context part of a Gofer class or instance declaration. This has a number of potentially useful applications because it enables the Gofer programs to use mutually `recursive' systems of dictionaries. One example of this is the ability to implement a large family of related functions using a group of classes instead of having to use a single class. The following example illustrates the technique with an alternative definition for the class Eq in which the (==) and (/=) operators are placed in different classes: class Neq a => Eq a where (==) :: a -> a -> Bool class Eq a => Neq a where (/=) :: a -> a -> Bool x/=y = not (x == y) [ASIDE: These declarations clash with those in the standard prelude and hence cannot actually be used in Gofer unless a modified version of the 79 Introduction to Gofer 14.4.2 `Recursive' dictionaries standard prelude is used instead.] If we then give instance declarations: instance Eq Int where (==) = primEqInt instance Neq Int and try to evaluate the expression "2==3" then the following system of dictionaries will be generated: d1 :: Eq Int d2 :: Neq Int +-----------+-----------+ +-----------+-----------+ | | | | | | +-->| primEqInt |d2::Neq Int+----->| defNeq d2 |d1::Eq Int +---+ | | | | | | | | | +-----------+-----------+ +-----------+-----------+ | | | +------------------------------<-------------------------------+ where the function "defNeq" is derived from the default definition in the class Neq and is equivalent to: defNeq d x y = not ((==) (#2 d) x y) Incidentally, if the instance declaration for Neq Int above had been replaced by: instance Neq a then the effect of these declarations would be similar to the standard definition of the class Eq, except that it would not be possible to override the default definition for (/=). In other words, this approach would give the same effect as defining (/=) as a top-level function rather than a member function in the class Eq: class Eq a where (==) :: a -> a -> Bool (/=) :: Eq a => a -> a -> Bool x /= y = not (x == y) There are other situations in which recursive dictionaries of the kind described above can be used. A further example is given in the following section. Unfortunately, the lack of restrictions on the form of class and instance declarations can also lead to problems in some (mostly pathological) cases. As an example, consider the class: class Bad [a] => Bad a where bad :: a -> a Without defining any instances of Bad, it is not possible to construct any dictionaries for instances of Bad: ? bad 2 ERROR: Cannot derive instance in expression *** Expression : bad d126 2 *** Required instance : Bad Int ? 80 Introduction to Gofer 14.4.2 `Recursive' dictionaries If however we add the instance declarations: instance Bad Int where bad = id instance Bad [a] where bad = id then any attempt to construct a dictionary for Bad Int will also require a dictionary for the superclass Bad [Int] and then for the superclass of that instance Bad [[Int]] etc... Since Gofer has only a finite amount of space for storing dictionaries, this process will eventually terminate when that space has been used up: ? bad 2 ERROR: Dictionary storage space exhausted ? [ASIDE: depending on the configuration of your particular version of Gofer and on the nature of the class and instance declarations that are involved, an alternative error message "ERROR: Too many type variables in type checker" may be produced instead of the message shown above.] From a practical point of view, this problem is unlikely to cause too many real difficulties: o Class declarations involving predicates such as those in the declaration of Bad are unlikely to be used in realistic programs. o All dictionaries are constructed before evaluation begins. This process is guaranteed to terminate because each new dictionary that is created uses up part of the space used to hold Gofer dictionaries. The construction process will either terminate successfully once complete, or be aborted as soon as all of the dictionary space has been used. It remains to see what impact (if any) this has on realistic programs, and if later versions of Gofer should be modified to impose some syntactic restrictions (as in Haskell) or perhaps some form of static checking of the contexts appearing in class and instance declarations. 14.4.3 Classes with multiple parameters --------------------------------------- Gofer is the first language to support the use of type classes with multiple parameters. This again is an experimental feature of the language, intended to make it possible to explore the claims from a number of researchers about the use of such classes. Initial experiments suggest that multiple parameter type classes are likely to lead to large numbers of problems with unresolved overloading. Ultimately, this may mean that such classes are only of practical use in explicitly typed languages, or alternatively that a more powerful and general defaulting mechanism (similar to that used in Haskell with numeric classes) is required to support user controlled overloading resolution. The following declaration introduces a class Iso whose elements are pairs of isomorphic types: 81 Introduction to Gofer 14.4.3 Classes with multiple parameters class Iso b a => Iso a b where iso :: a -> b The single member function "iso" represents the isomorphism mapping elements of type a to corresponding elements of type b. Note the `superclass' context in this declaration which formalises the idea that if a is isomorphic to b then b is also isomorphic to a. The class Iso therefore provides further examples of the recursive dictionaries described in the previous section. The fact that any type is isomorphic to itself can be described by the following instance declaration: instance Iso a a where iso x = x For example, the dictionary structure created in order to evaluate the expression "iso 2 = 3" is: d :: Iso Int Int +--------------+--------------+ | | | +-->| id |d::Iso Int Int+--+ | | | | | | +--------------+--------------+ | | | +------------------<-----------------+ ? iso 2 == 3 False (4 reductions, 11 cells) ? Our first taste of the problems to come occurs when we try to evaluate the expression "iso 2 == iso 3": ? iso 2 == iso 3 ERROR: Unresolved overloading *** type : (Eq a, Iso Int a) => Bool *** translation : \d130 d132 -> (==) d130 (iso d132 2) (iso d132 3) ? In this case, the "iso" function is used to map the integers 2 and 3 to elements of some type a, isomorphic to Int, and the values produced are then compared using (==) at the instance Eq a; there is no way of discovering what the value of a should be without using an explicit type signature. Further instances can be defined. The following two declarations are needed to describe the (approximate) isomorphism between lists of pairs and pairs of lists: instance Iso [(a,b)] ([a],[b]) where iso xs = (map fst xs, map snd xs) instance Iso ([a],[b]) [(a,b)] where iso (xs,ys) = zip xs ys 82 Introduction to Gofer 14.4.3 Classes with multiple parameters Unfortunately, even apparently straightforward examples give problems with unresolved overloading, forcing the use of explicit type declarations: ? iso [(1,2),(3,4)] ERROR: Unresolved overloading *** type : Iso [(Int,Int)] a => a *** translation : \d126 -> iso d126 [(1,2),(3,4)] ? (iso [(1,2),(3,4)]) :: ([Int],[Int]) ([1, 3],[2, 4]) (22 reductions, 64 cells) ? A second example of a multiple parameter type class is defined as follows: class Ord a => Collects a b where emptyCollection :: b addToCollection :: a -> b -> b listCollection :: b -> [a] The basic intuition is that the predicate Collects a b indicates that elements of type b can be used to represent collections of elements of type a. A number of people have suggested using type classes in this way to provide features similar to the (similarly named, but otherwise different) classes that occur in object-oriented languages. Obvious implementations involve the use of ordered lists or binary search trees defined by instances of the form: data STree a = Empty | Node a (STree a) (STree a) instance Collects a [a] where .... instance Collects a (STree a) where .... Once again, there are significant problems even with simple examples using these functions. As an example, the standard way of defining a function of type: Collects a b => [a] -> b mapping a list of values to a collection of those values using the higher order function "foldr": listToCollection = foldr addToCollection emptyCollection actually produces a function with ambiguous type: ? :t foldr addToCollection emptyCollection \d139 d140 -> foldr (addToCollection d140) (emptyCollection d139) :: (Collects c b, Collects a b) => [a] -> b ? which cannot be resolved, even with an explicit type declaration. 83 Introduction to Gofer 14.4.4 Overloading and numeric values 14.4.4 Overloading and numeric values ------------------------------------- One of the most common uses of overloading is to allow the use of the standard arithmetic operators such as (+), (*) etc. on the elements of a range of numeric types including integers and floating point values in addition to user defined numeric types such as arbitrary precision integers, complex and rational numbers, vectors and matrices, polynomials etc. In Haskell, these features are supported by a number of built-in types and a complex hierarchy of type classes describing the operations defined on the elements of each numeric type. As an experimental language, intended primarily for the investigation of general purpose overloading, Gofer has only two built-in numeric types; Int and Float (the second of which is not supported in all implementations). Similarly, although the Gofer system could be used to implement the full hierarchy of Haskell numeric classes, the standard prelude uses a single numeric type class Num defined by: class Eq a => Num a where -- simplified numeric class (+), (-), (*), (/) :: a -> a -> a negate :: a -> a fromInteger :: Int -> a The first four member functions (+), (-), (*), (/) are the standard arithmetic functions on instances of Num, whilst "negate" denotes unary negation. The final member function, fromInteger is used to coerce any integer value to the corresponding value in another instance of Num. An expression such as "fromInteger 3" is called an overloaded numeric constant and has type Num a => a indicating that it can be used as a value of any instance of Num. See below for examples. Both Float and Int are defined as instances of Num using primitive functions for integer and floating point arithmetic: instance Num Int where (+) = primPlusInt (-) = primMinusInt (*) = primMulInt (/) = primDivInt negate = primNegInt fromInteger x = x instance Num Float where (+) = primPlusFloat (-) = primMinusFloat (*) = primMulFloat (/) = primDivFloat negate = primNegFloat fromInteger = primIntToFloat These definitions make it possible to evaluate numeric expressions involving both types: ? 2 + 3 5 (3 reductions, 6 cells) 84 Introduction to Gofer 14.4.4 Overloading and numeric values ? 3.2 + 4.321 7.521 (3 reductions, 13 cells) ? Note however that any attempt to evaluate an expression mixing different arithmetic types is likely to cause a type error: ? 4.2 * 4 ERROR: Type error in application *** expression : 4.2 * 4 *** term : 4.2 *** type : Float *** does not match : Int ? Further problems occur when we try to define functions intended to be used with arbitrary instances of Num rather than specific numeric types. As an example of this, the standard prelude function "sum", roughly equivalent to: sum [] = 0 sum (x:xs) = x + sum xs has type [Int] -> Int, rather than the more general Num a => [a] -> a which could be used to find the sum of a list of numeric values in any instance of Num. The problem in this particular case is caused by the integer constant 0 in the first line of the definition. Replacing this with the expression fromInteger 0 leads to the following definition for a generic sum function of the required type: genericSum :: Num a => [a] -> a genericSum [] = fromInteger 0 genericSum (x:xs) = x + genericSum xs For example: ? genericSum [1,2,3] 6 (10 reductions, 18 cells) ? genericSum [1.0,2.0,3.0] 6.0 (11 reductions, 27 cells) ? The fromInteger function can also be used to solve the previous problem: ? 4.2 * fromInteger 4 16.8 (3 reductions, 13 cells) ? In Haskell, any integer constant k appearing in an expression is treated as if the programmer had actually written "fromInteger k" so that both of the preceding problems are automatically resolved. 85 Introduction to Gofer 14.4.4 Overloading and numeric values Unfortunately, this also creates some new problems; applying the function fromInteger to each integer constant in the previous examples causes problems with unresolved overloading: ? fromInteger 2 + fromInteger 3 ERROR: Unresolved overloading *** type : Num a => a *** translation : \d143 -> (+) d143 (fromInteger d143 2) (fromInteger d143 3) ? Once again, Haskell provides a solution to this problem in the form of a `default mechanism' for numeric types which, once the following problem has been detected, will typically `default' the unknown type represented by the type variable a above to be Int, so that the result is actually equivalent to the following: ? (fromInteger 2 + fromInteger 3) :: Int 5 (4 reductions, 8 cells) ? There are a number of problems with the Haskell default mechanism; both theoretical and practical. In addition, if a default mechanism of some form is used then it should also be capable of dealing with arbitrary user-defined type classes, rather than a small group of `standard' classes, in order to provide solutions to the unresolved overloading problems described in previous sections. Therefore, for the time being, Gofer does not support any form of default mechanism and overloaded numeric constants can only be obtained by explicit use of the fromInteger function. 14.4.5 Constants in dictionaries -------------------------------- The Gofer system constructs new dictionaries as necessary, and deletes them when they are no longer required. At any one time, there is at most one dictionary for each instance of a class. Coupled with lazy evaluation, this has a number of advantages for classes in which member functions are defined by variable declarations as in section 9.10. As an example, consider the class Finite defined by: class Finite a where members :: [a] The only member in this class is a list enumerating the elements of the type. For example: instance Finite Bool where members = [False, True] instance (Finite a, Finite b) => Finite (a,b) where members = [ (x,y) | x<-members, y<-members ] In order to overcome any problems with unresolved overloading, explicit type signatures are often needed to resolve overloading: ? members :: [Bool] 86 Introduction to Gofer 14.4.5 Constants in dictionaries [False, True] (6 reductions, 26 cells) ? length (members :: [((Bool,Bool),(Bool,Bool))]) 16 (103 reductions, 195 cells) ? In some cases, the required overloading is implicit from the context and no additional type information is required, as in the following example: ? [ x && y | (x,y) <- members ] [False, False, False, True] (29 reductions, 90 cells) ? We can also use the technique of passing a `dummy' parameter to resolve overloading problems in a function definition: size :: Finite a => a -> Int size x = length (members `asTypeOf` [x]) which calculates the number of elements of a finite type, given an arbitrary element of that type: ? size (True,False) 4 (31 reductions, 60 cells) ? Now consider the expression "size (True,False) + size (True,False)". At first glance, we expect this to repeat the calculation in the previous example two times, requiring approximately twice as many reductions and cells as before. However, before this expression is evaluated, Gofer constructs a dictionary for Finite (Bool,Bool). The evaluation of the first summand forces Gofer to evaluate the value for "members" in this dictionary. Since precisely the same dictionary is used to calculate the value of the second summand, the evaluation of "members" is not repeated and the complete calculation actually uses rather fewer reductions and cells: ? size (True,False) + size (True,False) 8 (51 reductions, 90 cells) ? On the other hand, repeating the original calculation gives exactly the same number of reductions and cells as before, because the dictionaries constructed at the beginning of each calculation are not retained for use in subsequent calculations. We can force Gofer to construct specific dictionaries whilst reading from a file of definitions, so that they are not deleted at the end of each calculation, using an explicitly typed variable definition such as: 87 Introduction to Gofer 14.4.5 Constants in dictionaries boolBoolMembers = members :: [(Bool,Bool)] This forces Gofer to construct the dictionary Finite (Bool,Bool) when the file of definitions is loaded and prevents it from being deleted at the end of each calculation. Having loaded a file containing this definition, the first two attempts to evaluate "size (True,False)" give: ? size (True,False) 4 (31 reductions, 60 cells) ? size (True,False) 4 (20 reductions, 32 cells) ? 14.4.6 The monomorphism restriction ----------------------------------- This section describes a technique used to limit the amount of overloading used in the definition of certain values to avoid a number of technical problems. This particular topic has attracted quite a lot of attention within the Haskell community where it is affectionately known as the `dreaded monomorphism restriction'. Although the initial formulation of the rule was rather cumbersome and limiting, the current version used in both Gofer and Haskell is unlikely to cause any problems in practice. In addition, many of the examples used to motivate the need for the monomorphism restriction in Haskell occur as a result of the use of implicitly overloaded numeric constants, described in section 14.4.4, and hence do not occur in Gofer. The monomorphism restriction takes its name from the way in which it limits the amount of polymorphism that can be used in particular kinds of declaration. Although we touch on this point in the following discussion, the description given here uses an equivalent, but less abstract approach, based on observations about the implementation of overloaded functions. Basic ideas: ------------ As we have seen, the implementation of overloading used by Gofer depends on being able to add extra arguments to a function definition to supply the required dictionary parameters. For example, given a function definition such as: isElement x [] = False isElement x (y:ys) = x==y || isElement x ys we first add a dictionary parameter for the use of the overloaded (==) operator on the right hand side, obtaining: isElement x [] = False isElement x (y:ys) = (==) d x y || isElement x ys Finally, we have to add the variable d as a new parameter for the function isElement, on both the left and right hand sides of the 88 Introduction to Gofer 14.4.6 The monomorphism restriction definition: isElement d x [] = False isElement d x (y:ys) = (==) d x y || isElement d x ys The monomorphism restriction imposes conditions which prevent this last step from being used for certain kinds of value binding. Declaration groups: ------------------- Before giving the full details, it is worth pointing out that, in general, the monomorphism restriction affects groups of value declarations rather than just individual definitions. To illustrate this point, consider the function definitions: f x y = x==y || g x y g x y = not (f x y) Adding an appropriate dictionary parameter for the (==) operator gives: f x y = (==) d x y || g x y g x y = not (f x y) The next stage is to make this dictionary variable into an extra parameter to the function f wherever it appears, giving: f d x y = (==) d x y || g x y g x y = not (f d x y) But now the right hand side of the second definition mentions the dictionary variable d which must therefore be added as an extra parameter to g: f d x y = (==) d x y || g d x y g d x y = not (f d x y) In other words, if dictionary parameters are added to any particular function definition, then each use of that function in another definition will also be require extra dictionary parameters. As a result, the monomorphism restriction has to be applied to the smallest groups of declarations such that any pair of mutually recursive bindings are in the same group. As the example above shows, if one (or more) of the bindings in a given declaration group is affected by the monomorphism restriction so that the appropriate dictionary parameters cannot be added as parameters for that definition, then the same condition must also be imposed on all of the other bindings in the group. [Adding the extra parameter to f in the example forces us to add an extra parameter for g; if extra parameters were not permitted for g then they could not be added to f.] 89 Introduction to Gofer 14.4.6 The monomorphism restriction Restricted bindings: -------------------- There are three main reasons for avoiding adding dictionary parameters to a particular value binding: o Dictionary parameters unnecessary. If the dictionary values are completely determined by context then it is not necessary to pass the appropriate values as dictionary parameters. For example, the function definition: f x = x == 0 || x == 2 can be translated as: f x = (==) {dict} x 0 || (==) {dict} x 2 where, in both cases, the symbol {dict} denotes the dictionary for Eq Int. As a further optimisation, once the dictionary is fully determined, this can be simplified to: f x = primEqInt x 0 || primEqInt x 2 o Dictionary parameters cannot be added in a pattern binding. One potential solution to this problem would be to replace the pattern binding by an equivalent set of function bindings. In practice, we do not use this technique because it typically causes ambiguity problems, as illustrated by the pattern binding: (plus,times) = ((+), (*)) Translating this into a group of function bindings gives: newVariable = ((+), (*)) plus = fst newVariable -- fst (x,_) = x times = snd newVariable -- snd (_,y) = y The type of newVariable is (Num a, Num b) => (a->a->a, b->b->b) so that the correct translation of these bindings using two dictionary variables gives: newVariable da db = ((+) da, (*) db) plus da db = fst (newVariable da db) times da db = snd (newVariable da db) and hence the correct types for plus and times are: plus :: (Num a, Num b) => a -> a -> a times :: (Num a, Num b) => b -> b -> b both of which are ambiguous. o Adding dictionary parameters may translate a variable definition into a function definition, loosing the benefits of shared evaluation. As an example, consider the following definition using the function "size" and the class Finite described in the previous section: 90 Introduction to Gofer 14.4.6 The monomorphism restriction twiceSize x = n + n where n = size x Since the variable n is defined using a local definition, we would not expect to have to evaluate size x more than once to determine the value of twiceSize. However, adding extra dictionary parameters without restriction gives: twiceSize d x = n d + n d where n d = size d x Now that n has been replaced by a function, the evaluation will be repeated, once for each occurrence of the expression "n d". In order to avoid this kind of problem, the monomorphism restriction does not usually allow extra parameters to be added to a variable definition. Thus the original definition above will be translated to give: twiceSize d x = n + n where n = size d x Note that the same rule is applied to variable definitions at the top-level of a file of definitions, resulting in an error if any dictionary parameters are required for the right hand side of the definition. As an example of this: twiceMembers = members ++ members which produces an error message of the form: ERROR "ex" (line 157): Unresolved top-level overloading *** Binding : twiceMembers *** Inferred type : [_7] *** Outstanding context : Finite _7 ? [COMMENT: A type expression of the form _n (such as _7 in this particular example) represents a fixed (i.e. monomorphic) type variable.] In the case of a variable declaration, the monomorphism restriction can be overcome by giving an explicit type signature including an appropriate context, to indicate that the variable defined is intended to be used as an overloaded value. In this case, we need only include the declaration: twiceMembers :: Finite a => [a] in the file containing the definition for twiceMembers to suppress the previous error message and allow the function to be used as a fully overloaded variable. Note that the monomorphism restriction interferes with the use of polymorphism. For example, the definition: aNumber = length (twiceMembers::[Bool]) + length (twiceMembers::[(Bool,Bool)]) where twiceMembers = members ++ members 91 Introduction to Gofer 14.4.6 The monomorphism restriction will not be accepted because the monomorphism restriction forces the local definition of "twiceMembers" to be restricted to a single overloading (the dictionary parameter supplied to each use of members must be constant throughout the local definition): ERROR "ex" (line 12): Type error in type signature expression *** term : twiceMembers *** type : [(Bool,Bool)] *** does not match : [Bool] ? Once again, this problem can be fixed using an explicit type declaration: aNumber = length (twiceMembers::[Bool]) + length (twiceMembers::[(Bool,Bool)]) where twiceMembers :: Finite a => [a] twiceMembers = members ++ members Formal definition: ------------------ The examples above describe the motivation for the monomorphism restriction, captured by the following definition: Dictionary variables will not be used as extra parameters in the definition of a value in a given declaration group G if: either: G includes a pattern binding or: G includes a variable declaration, but does not include an explicit type signature for any of the variables in the group. If neither of these conditions hold, then equivalent sets of dictionary parameters will be added to each declaration in the group. 92