我对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
。我认为执行这样的演绎是声明函数依赖的全部意义。
我认为执行这样的演绎是声明函数依赖的全部意义。
不幸的是,情况并非如此。在GADT的模式匹配过程中,fundep从不细化刚性类型变量。直觉上,它们在某些情况下应该这样做,但是它们没有这样做。我猜这是因为它们早在gts出现之前就出现在Haskell中了,所以没有先例。
这很烦人,我同意。这就是为什么我认为类型族通常是一个更好的主意。在我看来,如果可能的话,应该用类型族代替FunDeps(注意,在class C a b c | a->c, b->c
中我们确实不能这样做)。
顺便注意,在注入类型族扩展中可以观察到类似的失败:人们会期望GHC从F a ~ F b
推断出a ~ b
,但它没有。
无论如何,我想你现在想知道"如果这不是基金的重点,他们的重点是什么?"嗯,考虑:
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
的类型不再是模糊的,并且实例解析可以很好地工作。
(顺便说一下,现在这种歧义可以用类型应用程序在调用站点手动解决,但过去不是这样的。)