
On Tue, Dec 01, 2009 at 10:48:50AM -0500, Patrick LeBoutillier wrote:
Hi all,
I want to make all types that are instances of Integral instances of another type class, i.e:
{-# LANGUAGE FlexibleInstances #-}
class Intable a where toInt :: a -> Int
instance (Integral a) => Intable a where toInt l = fromIntegral l
This doesn't mean what you think it means. To do instance selection, (GHC at least) only looks at the part AFTER the =>. So instead of meaning "every type which is an instance of Integral should also be an instance of Intable", it actually means "every type is an instance of Intable; and oh yes, they had better be an instance of Integral too". This can make a big difference. For example, this may look fine, but it is not legal: instance (Integral a) => Intable a where toInt l = fromIntegral l instance (Fractional a) => Intable a where toInt l = round l This looks like it should be OK as long as we never use a type which is an instance of both Integral and Fractional (which is not likely). However, these instances are actually overlapping since the part before the => is not considered when deciding which instance to pick for a particular type. This also hints at an explanation for the error you got. When resolving the use of a type class method, GHC looks at the structure of the type in order to pick a type class instances to use. Once it has chosen an instance (by comparing the type to the part of the instance declaration to the right of the =>), it then considers any additional constraints generated by the left-hand side of the =>, and needs to choose a type class instance for each of those, and so on recursively. The danger is that this process might not terminate; in order to (conservatively) guard against this, GHC requires that any types mentioned on the left-hand side of the => are structurally smaller than types on the right-hand side; this ensures that the process will eventually terminate since types are finite. If you want to disable this check (and therefore introduce the possibility of an inifnitely recursing type checker) you can enable UndecidableInstances --- which isn't really as bad as some people make it out to be, but best avoided unless you really understand why you want it. It's hard to suggest what to do instead without knowing more about what you are specifically trying to do (it seems that your example was just a toy example to illustrate the problem), but you might consider using newtype wrappers. -Brent