Haskell函数依赖冲突

时间:2011-09-15 19:07:32

标签: haskell functional-dependencies

为什么这会导致冲突?

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禁止此操作,因为这意味着ab唯一确定?

2 个答案:

答案 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上做了,但它没有意义,它已经在新版本中得到了解决。