[Haskell-cafe] Ambiguous type signature in class declaration

Benjamin Franksen benjamin.franksen at bessy.de
Thu Apr 28 08:20:08 EDT 2005

On Thursday 28 April 2005 08:42, Bo Herlin wrote:
> Doh, i have another question:
> Lets say i do as you wrote:
>  > class CRank a b where
>  >   rank :: a -> b -> Maybe Integer -- Nothing means b is out of
>  > range or badly constructed
>  >   unrank :: a -> Integer -> Maybe b -- Nothing means rank is out
>  > of range
>  >
>  > class CCountable a where
>  >   count :: a -> Maybe Integer -- Nothing means infinity
> how do i make a test-class like this:
>  > class (CRank a b,CCountable a) => CTestable a where
>  >   testOne :: a -> Integer -> Bool
>  >   testUpTo :: a -> Integer -> Bool
>  >   testOne x r = ((unrank x r) >>= (rank x)) == (Just r)
>  >   testUpTo x mr = foldr1 (&&) (map (testOne x) [0..m])
>  >     where
>  >       m = f (count x)
>  >       f Nothing = mr
>  >       f (Just y) = min (y-1) mr
> this gives me:
> ERROR "./Cafe.lhs":14 - Undefined type variable "b"

It is the same problem as in the familiar 'show . read', that is, the 
intermediate type (what is to be read?) is lost. Similarly, in

  unrank x r >>= rank x

the compiler cannot infer what the type 'b' in the types of 'rank' and 
'unrank' should be. It is essentially the same problem as you had in 
the beginning, i.e. ambigous type variables.

Ask yourself: what is this code supposed to do, exactly? Which instance 
of 'class CRank a b' should be chosen in

  testOne Prime 1

the one for 'CRank Prime Int' or the one for 'CRank Prime Integer'? The 
compiler doesn't know that such a combination is not possible, due to 
teh superclass restriction.

Note that I wrote in the answer to myself, that the solution that 
factorizes 'count' to a superclass has teh disadvantage that it 
introduces the same restrictions as the solution using functional 
dependencies. Your problem would go away if you used functional 
dependencies, like this:

  class CRank a b | a -> b where ...

because then the compiler knows your intention, i.e. given a type 'a' 
and an instance of 'class CRank a b', then the 'b' is uniquely 
determined by this instance.

> If i remove the b like "class (CRank a,CCountable a) => CTestable a
> where" i get:
> ERROR "./Cafe.lhs":14 - Wrong number of arguments for class "CRank"

Of course, since you declared class CRank to take two type arguments 'a' 
and 'b'.

The general theme here is this: Using classes, i.e. overloading, you 
essentially say to the compiler: "Please chose the 'right' 
implementation, depending on the argument and/or result types of my 
functions!" For this to work, the compiler needs enough information to 
make the choice. Type inference can only find out a type, if it has a 
term on which to perform the inference. But in 'unrank x r >>= rank x' 
there are no longer any terms of type 'b' (the second type argument to 


More information about the Haskell-Cafe mailing list