7

这是标准的 Functor 实例Either a

instance Functor (Either a) where
        fmap _ (Left x) = Left x
        fmap f (Right y) = Right (f y)

在加载到 GHCi 时添加 as-pattern 会导致编译错误:

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

为什么这不起作用?

4

4 回答 4

11

fmap有签名(a -> b) -> f a -> f b,即它必须允许ab不同。在您的实现中,a并且b只能是相同的,因为它返回作为参数传递的相同内容。所以GHC抱怨。

于 2012-08-21T13:57:30.457 回答
2

例如Either afmap具有以下类型:

(i -> j) -> Either a i -> Either a j

在这个等式中:

fmap _ (Left x) = Left x

已知第二个参数是 typeEither a i并且与 pattern 匹配Left x。我们x取出,并应用Left到它得到结果fmap

诀窍是Left等式左边的和Left右边的不是一回事!在 LHSLeft上是这个构造函数:

Left :: a -> Either a i

而在 RHS 上Left是这个构造函数:

Left :: a -> Either a j

模式中使用的 RHSLeft不会匹配fmap' 的第二个参数的类型Either a i,并且 LHSLeft不会构造所需类型的值作为fmap' 结果Either a j

因此Left,从语义上讲,无法对这两种事物使用相同的方法;您必须构建一个Left x :: Either a j包含xLeft x :: Either a i. 在操作上,Haskell 实现可能会或可能不会相同地表示这两个术语,并且可能会或可能不会共享具有不同类型的两个值的单个内存表示,并且可能会或可能不会足够聪明以优化构造一个新值的表示方式与它现有的另一个值相同。但是这些实现问题与程序的含义不同,类型检查器的角色纯粹与含义有关。

于 2012-08-22T01:52:20.107 回答
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
于 2012-08-21T13:58:29.097 回答
2

如果你专门化fmapto的签名Either l,你会得到:

fmap :: (a -> b) -> Either l a -> Either l b

这意味着Left r您在 case 语句左侧进行模式匹配的 必须具有 type Either l a。但是,您不能按原样返回它,因为您必须返回一个Either l b. 这需要将左侧的值重新包装在一个新的值中,Left以便编译器可以推断它正在返回一个新生成的值Either,该值可能具有不同的类型Right

于 2012-08-21T19:24:00.260 回答