类型检查失败,带有as-pattern

时间:2018-03-26 18:39:43

标签: haskell ghc type-inference functor

我为我的自定义Functor类型编写了Either个实例,名为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。

1 个答案:

答案 0 :(得分:8)

你偶然发现了一个微妙的小错误。看一下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。在你的情况下,这是一个类似的概念。您提取的FirstOr a b,但您在右侧构建的FirstOr a c