使用 as-pattern 类型检查失败

Type check fails with as-pattern

我为我的自定义 Either 类型编写了一个 Functor 实例,称为 Or,它工作得很好,就像这样:

data Or a b = First a
            | Second b
            deriving (Eq, Show)

instance Functor (Or a) where
  fmap _ (First x)  = First x
  fmap f (Second b) = Second $ f b

然而,当我使用 as-pattern 时,程序不再进行类型检查:

instance Functor (Or a) where
  fmap _ first@(First _) = first
  fmap f (Second b)      = Second $ f b

错误信息:

error:
    • Couldn't match type ‘a1’ with ‘b’
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> Or a a1 -> Or a b
        at /home/me/haskell/functors/src/Lib.hs:139:3-6
      ‘b’ is a rigid type variable bound by
        the type signature for:
          fmap :: forall a1 b. (a1 -> b) -> Or a a1 -> Or a b
        at /home/me/haskell/functors/src/Lib.hs:139:3-6
      Expected type: Or a b
        Actual type: Or a a1
    • In the expression: first
      In an equation for ‘fmap’: fmap _ first@(First _) = first
      In the instance declaration for ‘Functor (Or a)’
    • Relevant bindings include
        first :: Or a a1
          (bound at /home/me/haskell/functors/src/Lib.hs:139:10)
        fmap :: (a1 -> b) -> Or a a1 -> Or a b
          (bound at /home/me/haskell/functors/src/Lib.hs:139:3)
    |
139 |   fmap _ first@(First _) = first
    |                            ^^^^^

这让我感到惊讶,因为我假设 First xfirst 具有相同的类型。

出现这种类型错误的原因是什么?

我正在使用 GHC 8.2.2。

您偶然发现了一个细微的小错误。看一下 First.

的类型
First :: a -> Or a b

现在,我们通常会省略 forall 个量词,但这对所有 ab 都是正确的。这将很重要。

fmap1 _ (First x) = First x

fmap2 _ first @ (First _) = first

让我们应用一些明确的类型签名。 (以下不是实际的 Haskell 代码,因为模式中不能有类型签名。但它说明了这一点。)

fmap1 :: (b -> c) -> Or a b -> Or a c
fmap1 _ ((First :: a -> Or a b) x) = (First :: a -> Or a c) x

fmap2 :: (b -> c) -> Or a b -> Or a c
fmap2 _ (first :: Or a b) @ (First _) = (first :: Or a b)

因此在 fmap2 中,first 的类型为 Or a b,但您需要一个 Or a c 才能使一切正常。在第一个示例中,它 看起来 就像您只是取消应用然后重新应用 First,但您实际上是在应用不同(但相关)的功能。这就像你如何做 show 1show "A"。这是对碰巧共享名称 show 的两个不同函数的两次不同调用。就您而言,这是一个类似的概念。您正在提取的 First 是一个 Or a b,但是您在 right-hand-side 上构建的 First 是一个 Or a c.