为什么在工作函数中添加as-pattern会导致编译错误?

时间:2012-08-21 13:45:19

标签: haskell types as-pattern

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

为什么这不起作用?

4 个答案:

答案 0 :(得分:11)

fmap具有签名(a -> b) -> f a -> f b,即必须允许ab不同。在您的实现中,ab只能是相同的,因为它返回作为参数传递的相同内容。所以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 fmapEither a i的第二个参数Left的类型不匹配,而LHS fmap不构造值所需类型为Either a j的结果Left

因此从语义上讲,没有办法对这两件事使用相同的Left x :: Either a j;您必须构建一个包含x中找到的Left x :: Either a i的新{{1}}。 操作,Haskell实现可能或可能不会相同地表示这两个术语,并且可能或可能不能共享具有不同类型的两个值的单个内存中表示,并且可能是也可能不是足够巧妙地优化掉一个新值的构造,该值将与其已有的另一个值相同。但是那些实现问题与意味着的程序不同,而类型检查器的角色完全与意义有关。