为什么这会导致冲突?
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
请注意,如果删除功能依赖,代码将编译。
我的印象是功能依赖只应该禁止像下面这样的东西,实际上,它会编译!
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
相同的b
参数,但不同的a
参数。不应该b -> a
禁止此操作,因为这意味着a
由b
唯一确定?
答案 0 :(得分:15)
您是否尝试使用第二个版本?我猜测在实例编译时,当你调用foo
时,你会开始出现歧义和重叠错误。
这里最大的绊脚石是,fundeps不会像你期望的那样与类型变量交互 - 实例选择并不真正寻找解决方案,它只是通过尝试统一而盲目匹配。具体来说,当您撰写Foo a a
时,a
完全是任意的,因此可以使用类似b -> b
的类型进行统一。当第二个参数的格式为b -> b
时,它会匹配两个实例,但是fundeps说在一种情况下第一个参数应该是b -> b
,而另一个参数应该是b
}。因此,冲突。
由于这显然让人感到惊讶,如果您尝试使用第二个版本会发生以下情况:
bar = foo () ()
导致:
Couldn't match type `Bool' with `()'
When using functional dependencies to combine
Foo Bool a,
...因为fundep通过第二个实例说明任何类型作为第二个参数唯一地确定Bool
为第一个。因此,第一个参数必须是Bool
。
bar = foo True ()
导致:
Couldn't match type `()' with `Bool'
When using functional dependencies to combine
Foo a a,
...因为fundep通过第一个实例说,任何类型作为第二个参数唯一地确定第一个相同的类型。因此,第一个参数必须是()
。
bar = foo () True
导致两个实例导致错误,因为这次他们同意第一个参数应为Bool
。
bar = foo True True
导致:
Overlapping instances for Foo Bool Bool
arising from a use of `foo'
...因为两个实例都满足,因此重叠。
很有趣,是吗?
答案 1 :(得分:2)
第一个例子是任何 a
,然后fundep会给你一个a
。这意味着它将排除其他任何东西,因为其他任何东西都应该与该自由变量统一,因此强制选择该实例。
编辑:最初我建议第二个例子有效。它在ghc 7.0.4上做了,但它没有意义,它已经在新版本中得到了解决。