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?

8
  • Just to make sure. If you remove both of the first two instances there's still an error, right? Commented Feb 13, 2015 at 19:04
  • @genisage Yes, it depends only on the last two instances Commented Feb 13, 2015 at 19:07
  • I can't reproduce the second part here. instance C (l i j) (r i j) i causes a conflict for me. (on ghc 7.8.3) Commented Feb 13, 2015 at 19:30
  • @genisage No conflict here, also ghc(i) 7.8.3 Commented Feb 13, 2015 at 19:56
  • @ØrjanJohansen I tried it again and still got a conflict. I'm currently using windows and the Haskell platform. Commented Feb 13, 2015 at 20:05

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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).
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.