Here's Either a
的标准Functor实例:
instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
在加载到GHCi中时,以as模式添加会导致编译错误:
instance Functor (Either a) where
fmap _ z@(Left x) = z -- <-- here's the as-pattern
fmap f (Right y) = Right (f y)
Couldn't match expected type `b' against inferred type `a1'
`b' is a rigid type variable bound by
the type signature for `fmap' at <no location info>
`a1' is a rigid type variable bound by
the type signature for `fmap' at <no location info>
Expected type: Either a b
Inferred type: Either a a1
In the expression: z
In the definition of `fmap': fmap _ (z@(Left x)) = z
为什么这不起作用?
答案 0 :(得分:11)
fmap
具有签名(a -> b) -> f a -> f b
,即必须允许a
和b
不同。在您的实现中,a
和b
只能是相同的,因为它返回作为参数传递的相同内容。所以GHC抱怨道。
答案 1 :(得分:2)
我最好的猜测是失败,因为z
代表等式两边的不同类型:
整体类型为fmap :: (a -> b) -> Either t a -> Either t b
左侧是z :: Either t a
,z :: Either t b
似乎允许Left x
在同一个等式中有多个不同类型,但z
不是。
这个实现也失败了,显然是出于同样的原因:
instance Functor (Either a) where
fmap f (Right y) = Right (f y)
fmap _ z = z
Couldn't match expected type `b' against inferred type `a1'
`b' is a rigid type variable bound by
the type signature for `fmap' at <no location info>
`a1' is a rigid type variable bound by
the type signature for `fmap' at <no location info>
Expected type: Either a b
Inferred type: Either a a1
In the expression: z
In the definition of `fmap': fmap _ z = z
答案 2 :(得分:2)
如果您将fmap
的签名专门设为Either l
,则会获得:
fmap :: (a -> b) -> Either l a -> Either l b
这意味着您在case语句左侧进行模式匹配的Left r
必须具有类型Either l a
。但是,您不能按原样返回,因为您必须返回Either l b
。这需要在新的Left
中重新包装左侧值,以便编译器可以推断它正在返回可能具有Either
值的不同类型的新创建的Right
答案 3 :(得分:2)
对于Either a
实例,fmap
具有以下类型:
(i -> j) -> Either a i -> Either a j
在这个等式中:
fmap _ (Left x) = Left x
已知第二个参数属于Either a i
类型并且匹配模式Left x
。我们将x
取出,并将Left
应用于fmap
以获得Left
的结果。
诀窍在于,等式左边的Left
与右侧的Left
不同!在LHS Left :: a -> Either a i
上是这个构造函数:
Left
而在RHS上Left :: a -> Either a j
是这个构造函数:
Left
模式中使用的RHS fmap
与Either a i
的第二个参数Left
的类型不匹配,而LHS fmap
不构造值所需类型为Either a j
的结果Left
。
因此从语义上讲,没有办法对这两件事使用相同的Left x :: Either a j
;您必须构建一个包含x
中找到的Left x :: Either a i
的新{{1}}。 操作,Haskell实现可能或可能不会相同地表示这两个术语,并且可能或可能不能共享具有不同类型的两个值的单个内存中表示,并且可能是也可能不是足够巧妙地优化掉一个新值的构造,该值将与其已有的另一个值相同。但是那些实现问题与意味着的程序不同,而类型检查器的角色完全与意义有关。