考虑以下代码
data Foo f where
Foo :: Foo Int
class DynFoo t where
dynFoo :: Foo f -> Foo t
instance DynFoo Int where
dynFoo Foo = Foo
obsFoo :: (DynFoo t) => Foo f -> Foo t
obsFoo = dynFoo
useDynFoo :: Foo f -> Int
useDynFoo (obsFoo -> Foo) = 1
useDynFoo
中的模式匹配应该限制使用obsFoo
来使用Foo f -> Foo Int
类型,这会导致它搜索DynFoo Int
的实例。但是,它会针对未知DynFoo t
搜索t
的实例,并自然会失败。
No instance for (DynFoo t0) arising from a use of ‘obsFoo’
The type variable ‘t0’ is ambiguous
但是,如果我将useDynFoo
的定义更改为
useDynFoo :: Foo f -> Int
useDynFoo (obsFoo -> (Foo :: Foo Int)) = 1
然后它突然起作用,即使我的类型签名完全是多余的。
那么,为什么会发生这种情况,如何在不必提供类型签名的情况下使用obsFoo
?
答案 0 :(得分:2)
如果用明确的case
写出来更清楚(视图模式非常模糊WRT类型信息流):
useDynFoo :: Foo f -> Int
useDynFoo foof = case obsFoo foof of
Foo -> 1
此处,f ~ Int
可以完全访问信息1
。好吧,但这不是我们需要这些信息的地方:我们已经在obsFoo foof
处需要它。并且信息无法到达:GADT模式匹配充当完整的“类型信息二极管”,即来自外部的任何信息都可以在匹配范围内使用,但是没有来自内部的信息可以不使用。 (显然有充分的理由,因为这些信息只能在运行时确认,当你实际上拥有 GADT构造函数来获取它时。)
更有趣的问题是,如果添加:: Foo Int
签名,为什么会这样?嗯,特别是视图模式的奇怪之处。请参阅以下内容 not work:
useDynFoo :: Foo f -> Int
useDynFoo foof = case obsFoo foof of
(Foo :: Foo Int) -> 1
正如您所说,这些信息完全是多余的。
然而事实证明,这种观点模式实际上相当于将签名放在案例的另一部分上:
useDynFoo :: Foo f -> Int
useDynFoo foof = case obsFoo foof :: Foo Int of
Foo -> 1
这是一双非常不同的鞋,因为现在Foo Int
不在GADT模式匹配中。
我不知道为什么用这样的签名看待模式,也许是为了使这种模式成为可能。
答案 1 :(得分:1)
使用GADT时,类型签名不是多余的。请注意GHC Users Guide: GADTs
的最后一个要点