A wee bit off topic... but bear with me. Oleg points out a distinction between declaring a class with functional dependencies and implementing a class with functional dependencies. Judging from my experience, it might behoove those wrestling with type classes and FDs to emphasize that the class declaration also merely declares the functional dependencies and does not guarantee them as type-level functions. Moreover, instances implementing the class implement the functional dependencies as well. However, just because GHC accepts the instances as satisfying the functional dependencies, it doesn't necessarily guarantee that the functional dependencies can be aggressively used to resolve polymorphism--let me elaborate on this last point. Consider
class C a b | a -> b where foo :: a -> b instance C Int Int where foo = id instance Num a => C Bool a where foo _ = 3 GHC 6.7.20070214 accepts this code with fglasgow-exts and undecidable instances. I usually read the functional dependencies as "a determines b" (and I suspect many other people do as well). Unfortunately, that is not the guaranteed by the functional dependency analyzer. What is guaranteed is that any two instances of C do not together contradict the functional dependencies. Given C Bool x, I cannot infer what x is, though I had thought that "a determines b". When I was exercising my prefrontal Olegial cortex in writing my own static record library a la Hlist, I learned this lesson the hard way. Hopefully this saves the reader some trouble. Motto: "appeasing the functional dependency analyzer DOES NOT mean that the type class is actually a type function". Perhaps ATs do have this quality? I'm not sure--but if they do I will definitely be a fan. On 3/28/07, [EMAIL PROTECTED] <[EMAIL PROTECTED]> wrote:
>>> class T root pos sel | pos -> root, root -> sel where >>> f :: pos -> sel -> Bool >>> instance T root (Any root) sel > But the same applies to the second functional dependency and the type > variable sel. Every instantiation of root determines the instantiation > of sel. And that forbids instance T Int (Any Int) Bool and instance T > Int (Any Int) Int inside the same scope, doesn't it? Indeed that is your intent, expressed in the functional dependency. It may help to think of a class declaration as an `interface' and of the set of instances as an `implementation' (of the type class). In the example above, the "class T root pos sel" _declares_ a ternary relation T and specifies some `constraints'. The set of instances of T (in our example, there is only one instance) specifies the triples whose set defines the relation T. In Herbrand interpretation, an unground instance instance C1 x y => C (Foo x) (Bar y) corresponds to a set of instances where the free type variables are substituted by all possible ground types provided the instance constraints (such as C1 x y) hold. In our example, an unground instance |instance T root (Any root) sel| is equivalent to a set of ground instances where |root| and |sel| are replaced with all possible ground types. Including instance T Int (Any Int) Bool instance T Int (Any Int) Int These two instances are in the model for `instance T root (Any root) sel'. A set of instances, an implementation of a type class, must satisfy the interface, that is, constraints imposed by the class declaration, including the functional dependency constraints. In our example, any implementation of T must satisfy root -> sel constraints. The above two instances show there exists a model of T where the functional dependency is violated. That's why both GHC 6.4 and Hugs reject the instance. Again, it is a mystery why GHC 6.6 accepts it. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe