Ambiguous type signature in class declaration

Hi I am trying to make a class like this:
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 count :: a -> Maybe Integer -- Nothing means infinity
with possible instances like
data DPrime = Prime deriving (Show)
instance CRank DPrime Integer where rank Prime x = Nothing -- to be implemented: (rank Prime 11) should give (Just 4) unrank Prime r = Nothing -- to be implemented: (unrank Prime 4) should give (Just 11) count _ = Nothing -- Nothing means infinity
and
data DFibonacci = Fibonacci deriving (Show)
instance CRank DFibonacci Integer where rank Fibonacci x = Nothing -- to be implemented unrank Fibonacci r = Nothing -- to be implemented count _ = Nothing -- Nothing means infinity
but all i get is ERROR "./Cafe.lhs":8 - Ambiguous type signature in class declaration *** ambiguous type : CRank a b => a -> Maybe Integer *** assigned to : count Any suggestions anyone? Thanks in advance /Bo

See: http://haskell.org/hawiki/FunDeps
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 count :: a -> Maybe Integer -- Nothing means infinity
[snip]
but all i get is
ERROR "./Cafe.lhs":8 - Ambiguous type signature in class declaration *** ambiguous type : CRank a b => a -> Maybe Integer *** assigned to : count
Any suggestions anyone?

On Wednesday 27 April 2005 19:12, Bo Herlin wrote:
I am trying to make a class like this:
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 count :: a -> Maybe Integer -- Nothing means infinity
but all i get is
ERROR "./Cafe.lhs":8 - Ambiguous type signature in class declaration *** ambiguous type : CRank a b => a -> Maybe Integer *** assigned to : count
The type variable 'b' does not appear on the right side of the '=>' in the type of 'count'. The compiler complains about an 'ambigous type signature', because the type of 'b', and hence of 'count', cannot be determined from its arguments. Thus, if there are instances instance CRank Prime Integer where ... instance CRank Prime Int where ... then these would have different implementations for 'count'. Which one should be chosen if you write count Prime the infinite result for Integers or the (presumably) finite result for Ints? Functional dependencies only help if you never want to declare both of the above instances. If this is not the case (and therefore you don't want to use a fundep 'a -> b'), you can disambiguate the signature by giving it a second (phantom) argument to indicate the type: class CRank a b where ... count :: a -> b -> Maybe Integer -- implementations must not evaluate 2nd argument and call it like this let n = count Prime (undefined::Integer) Admittedly, the extra argument is not very nice and neither is the 'undefined'. Another trick is to split the class: class Countable a where count :: a -> Maybe Integer -- Nothing means infinity class Countable a => CRank a b where rank :: ... ... Ben

On Wednesday 27 April 2005 22:12, Benjamin Franksen wrote:
Another trick is to split the class:
class Countable a where count :: a -> Maybe Integer -- Nothing means infinity
class Countable a => CRank a b where rank :: ... ...
This solution has similar disadvantages as the fundep variant, that is, there can be only one instance for 'Countable Prime' and not two different ones (like an infinite count for Integer Primes and a finite one for Ints). In fact, this seems to be a standard way to achieve the effect of fundeps without actually using them (although I have read somewhere that it doesn't work in all cases). Ben

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" 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" As a Haskell-newbee i get totally confused. /Bo

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 CRank). HTH, Ben

Hi again Next approach:
module Cafe where
class CRankable a where rank :: a b -> b -> Maybe Integer -- Nothing means b is out of range or badly constructed unrank :: a b -> Integer -> Maybe b -- Nothing means rank is out of range count :: a b -> Maybe Integer -- Nothing means infinity
This works ok:
data CPrime a = Prime deriving (Show)
instance CRankable CPrime where rank Prime x = Nothing unrank Prime r = Nothing count Prime = Nothing
But once i start returning anything but Nothing like:
data CPrime' a = Prime' deriving (Show)
instance CRankable CPrime' where rank Prime' x = Just x unrank Prime' r = Just r count Prime' = Nothing
i get: ERROR "./Cafe.lhs":26 - Inferred type is not general enough *** Expression : rank *** Expected type : CRankable CPrime' => CPrime' a -> a -> Maybe Integer *** Inferred type : CRankable CPrime' => CPrime' Integer -> Integer -> Maybe Integer so where exactly do i say a is an Integer? /Bo
participants (3)
-
Benjamin Franksen
-
Bo Herlin
-
robert dockins