6

Consider the following Haskell code:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances,
    FunctionalDependencies #-}

class C a b c | a b -> c

instance C (l (i,j)) (r i j)   j
instance C (l i j)   (r (i,j)) j
-- Conflict between the following two lines
instance C (l (i,j)) (r (i,j)) j  
instance C (l i j)   (r i j)   j

Here, GHC yields a functional dependencies error between the last two lines. If I drop any of the last two instance declarations, the code compiles. I tried an analogue using type families, which also produced a conflict. My first question is: Why do the last two lines conflict, while the other declarations all work fine together?

In addition, if I change the very last line to

instance C (l i j)   (r i j)   i

GHC accepts the code. This seems quite weird, since the only thing that changes is the dependent type variable c. Can somebody explain this behavior?

1 Answers1

6

The last two instances have a conflicting unification. Let me use completely different variable names:

C (a c (d,e)) (b c (d,e)) e
vs.
C (a c (d,e)) (b c (d,e)) (d,e)

In particular, your l from the third instance can be unified with a type constructor which already has an argument applied.

Changing your j to i makes the last one instead:

C (a c (d,e)) (b c (d,e)) c

I still don't understand why that doesn't give a complaint. Perhaps it's because you can assign the types such that c = e, but not such that e = (d,e) (that would give an infinite type which Haskell doesn't allow), but it still seems like a dubious thing to allow. Perhaps this is even a GHC bug.

The other instance combinations don't conflict because when you try to unify them, you end up with contradictions similar to the e = (d,e) above, but in the non-dependent parts, so they cannot match.

Ørjan Johansen
  • 18,119
  • 3
  • 43
  • 53
  • I'd agree with you. Experimenting, I found that `class C a b ; instance C (l (i,j)) (r (i,j)) ; instance C (l i j) (r i j)` is _accepted_ without mentioning the overlap (!?). Maybe now some overlaps are being detected only later, when a method is called (just guessing here). – chi Feb 13 '15 at 20:56
  • Thank you for pointing out the unification process. @chi: Indeed, if I try to write a class method `f :: a -> b -> c` I get a complaint abount incoherency when calling `f` – Nicolas Malebranche Feb 13 '15 at 22:37