Why does this result in a conflict?
class Foo a b | b -> a where
foo :: a -> b -> Bool
instance Eq a => Foo a a where
foo = (==)
instance Eq a => Foo a (a -> a) where
foo x f = f x == x
Note that the code will compile if I remove the functional dependecy.
I was under the impression that functional dependencies should only disallow stuff like the following, when in fact, it compiles!
class Foo a b | b -> a where
foo :: a -> b -> Bool
instance Eq a => Foo a a where
foo = (==)
instance Eq a => Foo Bool a where
foo _ x = x == x
Same b parameter, yet different a parameters. Shouldn’t b -> a disallow this, as this means a is uniquely determined by b?
Have you tried actually using the second version? I’m guessing that while the instances compile, you’ll start getting ambiguity and overlap errors when you call
foo.The biggest stumbling block here is that fundeps don’t interact with type variables the way you might expect them to–instance selection doesn’t really look for solutions, it just blindly matches by attempting unification. Specifically, when you write
Foo a a, theais completely arbitrary, and can thus unify with a type likeb -> b. When the second parameter has the formb -> b, it therefore matches both instances, but the fundeps say that in one case the first parameter should beb -> b, but in the other that it should beb. Hence the conflict.Since this apparently surprises people, here’s what happens if you try to use the second version:
bar = foo () ()results in:…because the fundep says, via the second instance, that any type as the second parameter uniquely determines
Boolas the first. So the first parameter must beBool.bar = foo True ()results in:…because the fundep says, via the first instance, that any type as the second parameter uniquely determines the same type for the first. So the first parameter must be
().bar = foo () Trueresults in errors due to both instances, since this time they agree that the first parameter should beBool.bar = foo True Trueresults in:…because both instances are satisfied, and therefore overlap.
Pretty fun, huh?