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
的类型不再有歧义,并且实例解析可以很好地工作。
(顺便说一句,现在这种歧义可以在调用现场用类型应用程序手动解决,但过去不是这样的。)
我对 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
的类型不再有歧义,并且实例解析可以很好地工作。
(顺便说一句,现在这种歧义可以在调用现场用类型应用程序手动解决,但过去不是这样的。)