GADT 类型参数未用于类型类解析
GADT type argument not being used for typeclass resolution
考虑以下代码
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
而无需提供类型签名?
使用 GADT 时类型签名不是多余的。请注意 GHC Users Guide: GADTs
的最后要点
如果你用显式写出来会更清楚case
(视图模式非常模糊 WRT 类型信息流):
useDynFoo :: Foo f -> Int
useDynFoo foof = case obsFoo foof of
Foo -> 1
在这里,f ~ Int
的信息对于 1
是完全可以访问的。好吧,但这不是我们需要这些信息的地方:我们已经在 obsFoo foof
需要它了。并且信息无法到达那里:GADT模式匹配充当一个完整的“类型信息二极管”,即任何来自外部的信息都可以在匹配范围内使用,但没有来自内部的信息不能使用。 (显然有充分的理由,因为该信息只能在运行时确认,当您实际上 有 一个 GADT 构造函数可以从中获取它时。)
比较有意思的问题是,为什么加上:: Foo Int
签名就可以了?好吧,这尤其是视图模式的怪异之处。看,以下 不 工作:
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 模式匹配中。
我不知道为什么带有签名的视图模式会这样脱糖,也许是为了让这个模式成为可能。
考虑以下代码
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
而无需提供类型签名?
使用 GADT 时类型签名不是多余的。请注意 GHC Users Guide: GADTs
的最后要点如果你用显式写出来会更清楚case
(视图模式非常模糊 WRT 类型信息流):
useDynFoo :: Foo f -> Int
useDynFoo foof = case obsFoo foof of
Foo -> 1
在这里,f ~ Int
的信息对于 1
是完全可以访问的。好吧,但这不是我们需要这些信息的地方:我们已经在 obsFoo foof
需要它了。并且信息无法到达那里:GADT模式匹配充当一个完整的“类型信息二极管”,即任何来自外部的信息都可以在匹配范围内使用,但没有来自内部的信息不能使用。 (显然有充分的理由,因为该信息只能在运行时确认,当您实际上 有 一个 GADT 构造函数可以从中获取它时。)
比较有意思的问题是,为什么加上:: Foo Int
签名就可以了?好吧,这尤其是视图模式的怪异之处。看,以下 不 工作:
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 模式匹配中。
我不知道为什么带有签名的视图模式会这样脱糖,也许是为了让这个模式成为可能。