In Haskell 98, contexts consist of class constraints on type variables applied to zero or more types, as in
f :: (Functor f, Num (f Int)) => f String -> f Int -> f IntIn class and instance declarations only type variables may be constrained. With the
-98
option,
any type may be constrained by a class, as in
g :: (C [a], D (a -> b)) => [a] -> bClasses are not limited to a single argument either (see Section 6.2.4).
In Haskell 98, instances may only be declared for
a data or newtype type constructor
applied to type variables.
With the -98
option, any type may be made an instance:
instance Monoid (a -> a) where ... instance Show (Tree Int) where ... instance MyClass a where ... instance C String whereThis relaxation, together with the relaxation of contexts mentioned above, makes the checking of constraints undecidable in general (because you can now code arbitrary Prolog programs using instances). To ensure that type checking terminates, Hugs imposes a limit on the depth of constraints it will check, and type checking fails if this limit is reached. You can raise the limit with the
-c
option,
but such a failure usually indicates that the type checker wasn't going to
terminate for the particular constraint problem you set it.Note that GHC implements a different solution, placing syntactic restrictions on instances to ensure termination, though you can also turn these off, in which case a depth limit like that in Hugs is used.
With the relaxation on the form of instances discussed in the previous section, it seems we could write
class C a where c :: a instance C (Bool,a) where ... instance C (a,Char) where ...but then in the expression c :: (Bool,Char), either instance could be chosen. For this reason, overlapping instances are forbidden:
ERROR "Test.hs":4 - Overlapping instances for class "C" *** This instance : C (a,Char) *** Overlaps with : C (Bool,a) *** Common instance : C (Bool,Char)However if the
+o
option is set,
they are permitted when one of the types is a substitution instance of
the other (but not equivalent to it), as in
class C a where toString :: a -> String instance C [Char] where ... instance C a => C [a] where ...Now for the type [Char], the first instance is used; for any type [t], where t is a type distinct from Char, the second instance is used. Note that the context plays no part in the acceptability of the instances, or in the choice of which to use.
The above analysis omitted one case, where the type t is a type variable, as in
f :: C a => [a] -> String f xs = toString xsWe cannot decide which instance to choose, so Hugs rejects this definition. However if the
+O
option is set,
this declaration is accepted, and the more general instance is selected,
even though this will be the wrong choice if f is later
applied to a list of Char.Hugs used to have a +m
option
(for multi-instance resolution,
if Hugs was compiled with MULTI_INST set),
which accepted more overlapping instances by deferring the choice between them,
but it is currently broken.
Sometimes one can avoid overlapping instances.
The particular example discussed above is similar to the situation described
by the Show class in the Prelude.
However there overlapping instances are avoided by adding the method
showList
to the class.
In Haskell 98, type classes have a single parameter; they may be thought of as sets of types. In Hugs, they may have one or more parameters, corresponding to relations between types, e.g.
class Isomorphic a b where from :: a -> b to :: b -> a
Multiple parameter type classes often lead to ambiguity. Functional dependencies (inspired by relational databases) provide a partial solution, and were introduced in Type Classes with Functional Dependencies, Mark P. Jones, In Proceedings of the 9th European Symposium on Programming, LNCS vol. 1782, Springer 2000.
Functional dependencies are introduced by a vertical bar:
class MyClass a b c | a -> b whereThis says that the b parameter is determined by the a parameter; there cannot be two instances of MyClass with the same first parameter and different second parameters. The type inference system then uses this information to resolve many ambiguities. You can have several dependencies:
class MyClass a b c | a -> b, a -> c whereThis example could also be written
class MyClass a b c | a -> b c whereSimilarly more than one type parameter may appear to the left of the arrow:
class MyClass a b c | a b -> c whereThis says that the c parameter is determined by the a and b parameters together; there cannot be two instances of MyClass with the same first and second parameters, but different third parameters.