GADT 匹配和功能依赖性未推断的类型

Type not inferred by GADT match and functional depenency

我对 Haskell 类型系统有一个我认为可能很常见的误解,但我找不到这个简单示例显示的特定类型检查失败的解释:

{-#LANGUAGE GADTs, FunctionalDependencies #-}

module GaF where

class XDeterminesY x y | x -> y
instance XDeterminesY Int Double

data Wrapper x where
    WrappedInt :: Wrapper Int

func :: XDeterminesY x y => Wrapper x -> y -> Double
func WrappedInt a = a

GHC 抱怨它无法从上下文 x ~ Int 中推断出 y ~ Double。我想象执行这样的推导是声明函数依赖的全部意义。

I imagined that to perform such a deduction was the whole point of declaring the functional dependency.

不幸的是,情况并非如此。 FunDeps 永远不会像在 GADT 上的模式匹配期间那样精炼刚性类型变量。直觉上,他们 应该 在某些情况下这样做,但他们没有这样做。我想这是因为它们出现在 Haskell 早在 GADT 出现之前就出现了,所以没有先例。

这很烦人,我同意。这就是为什么我认为类型族通常是一个更好的主意。在我看来,FunDeps 应该在可能的情况下被类型族取代(请注意,在 class C a b c | a->c, b->c 中我们确实不能这样做)。

请注意,在 内射 类型族扩展中可以观察到类似的失败:人们希望 GHC 从 F a ~ F b 推断出 a ~ b,但事实并非如此。

无论如何,我想你现在想知道“如果这不是 FunDeps 的重点,他们的重点是什么?”。好吧,考虑一下:

class C a b | a -> b where
   foo :: a -> b -> String
   bar :: a -> String

instance C Bool Int where ...

如果没有 FunDeps,像 bar True 这样的调用将是模棱两可的:C Bool Int 在一个实例中我们可以用来解决约束,但可能还有另一个。在这里,问题是 bar :: a -> String 类型不涉及 b,因此无法知道我们是否可以安全地提交实例。该类型被认为是不明确的。

与 FunDeps 相反,我们保证不会有其他实例,因此仅涉及 a 的类型不再有歧义,并且实例解析可以很好地工作。

(顺便说一句,现在这种歧义可以在调用现场用类型应用程序手动解决,但过去不是这样的。)