GADT类型参数未用于类型类分辨率

时间:2016-09-13 02:54:21

标签: haskell type-inference typeclass 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

2 个答案:

答案 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

的最后一个要点