我为我的自定义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 x
和first
具有相同的类型。
造成此类型错误的原因是什么?
我正在使用GHC 8.2.2。
答案 0 :(得分:8)
你偶然发现了一个微妙的小错误。看一下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
,但您在右侧构建的First
为Or a c
。