Haskell函数依赖冲突

Tho*_*ing 13 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
Run Code Online (Sandbox Code Playgroud)

请注意,如果我删除功能依赖性,代码将编译.

我的印象是功能依赖只应该禁止像下面这样的东西,实际上,它编译!

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
Run Code Online (Sandbox Code Playgroud)

相同的b参数,但不同的a参数.不应该b -> a禁止这一点,因为这意味着a唯一的决定b

C. *_*ann 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,
    
    Run Code Online (Sandbox Code Playgroud)

    ...因为fundep通过第二个实例说明任何类型作为第二个参数唯一地确定Bool为第一个.所以第一个参数必须是Bool.

  • bar = foo True () 结果是:

    Couldn't match type `()' with `Bool'
      When using functional dependencies to combine
        Foo a a,
    
    Run Code Online (Sandbox Code Playgroud)

    ...因为fundep通过第一个实例说,作为第二个参数的任何类型唯一地确定第一个参数的相同类型.所以第一个参数必须是().

  • bar = foo () True由于这两个实例导致错误,因为这次他们同意第一个参数应该是Bool.

  • bar = foo True True 结果是:

    Overlapping instances for Foo Bool Bool
      arising from a use of `foo'
    
    Run Code Online (Sandbox Code Playgroud)

    ...因为两个实例都满足,因此重叠.

很有趣,是吧?

  • @Daniel Wagner:编写那些永远无法工作的实例,但只要你不尝试使用它们就会被编译器接受,这是我所知道的一个众所周知的漏洞.这是为什么我更喜欢类型系列用于大多数目的的原因之一. (4认同)