
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