使用 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 x
和 first
具有相同的类型。
出现这种类型错误的原因是什么?
我正在使用 GHC 8.2.2。
您偶然发现了一个细微的小错误。看一下 First
.
的类型
First :: a -> Or a b
现在,我们通常会省略 forall
个量词,但这对所有 a
和 b
都是正确的。这将很重要。
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 1
和 show "A"
。这是对碰巧共享名称 show
的两个不同函数的两次不同调用。就您而言,这是一个类似的概念。您正在提取的 First
是一个 Or a b
,但是您在 right-hand-side 上构建的 First
是一个 Or a c
.
我为我的自定义 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 x
和 first
具有相同的类型。
出现这种类型错误的原因是什么?
我正在使用 GHC 8.2.2。
您偶然发现了一个细微的小错误。看一下 First
.
First :: a -> Or a b
现在,我们通常会省略 forall
个量词,但这对所有 a
和 b
都是正确的。这将很重要。
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 1
和 show "A"
。这是对碰巧共享名称 show
的两个不同函数的两次不同调用。就您而言,这是一个类似的概念。您正在提取的 First
是一个 Or a b
,但是您在 right-hand-side 上构建的 First
是一个 Or a c
.