[Haskell-beginners] Typeclass question

Brent Yorgey byorgey at seas.upenn.edu
Tue Dec 1 14:24:17 EST 2009


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


More information about the Beginners mailing list