将 Either 理解为函子

Understand Either as a Functor

查看 Either 是如何定义为函子的,我可以看到

derive instance functorEither :: Functor (Either a)

这对我来说是“你可以 map 一个 Either 只要你可以 map 它的元素。

但两者都不只有一个元素。如果没有 derive,这将如何实现?这是我尝试过的:

data Either a b = Left a | Right b

instance functorEither :: Functor (Either a)
  where
    map f (Right b) = Right $ f b
    map _ a = a

当然,类型在这里不起作用:

右边有这个签名:map :: forall a b. (a -> b) -> f a -> f b
然而,左派不行:map :: forall a b. (a -> b) -> f a -> f a


我的部分直觉是 Either a b 不是函子,只有 Either a 是函子。这就是为什么 mapRight 有效并忽略 Left

这并没有真正让我对它的实现方式有任何直觉。我仍然需要一种匹配两个构造函数的方法,不是吗?

另一方面,我认为用 identity 替换内部函数的 map 的实现在技术上对仿函数来说是守法的吗?如果你忽略它,是否满足组合定律?

虽然您提出的 Functor 实例定义确实无法编译,但并不是因为您所说的原因。而且它也是“本质上”正确的,只是没有以满足编译器的方式编写。

为方便起见,这里再次给出您的定义:

data Either a b = Left a | Right b

instance functorEither :: Functor (Either a)
  where
    map f (Right b) = Right $ f b
    map _ a = a

这是您在尝试编译时遇到的实际错误:

  Could not match type

    a02

  with type

    b1


while trying to match type Either a0 a02
  with type Either a0 b1
while checking that expression a
  has type Either a0 b1
in value declaration functorEither

where a0 is a rigid type variable
        bound at (line 0, column 0 - line 0, column 0)
      b1 is a rigid type variable
        bound at (line 0, column 0 - line 0, column 0)
      a02 is a rigid type variable
        bound at (line 0, column 0 - line 0, column 0)

我承认这有点难以解释,如果你没有预料到的话。但这与 Either amap 需要类型 forall b c. (b -> c) -> Either a b -> Either a c 这一事实有关。所以 map _ a = a 左边的 a 类型是 Either a b,而右边的类型是 Either a c - 这些是不同的类型(通常),因为 bc 可以是任何东西,所以你不能使用相同的变量 a 来表示每种类型的值。

This 问题,虽然是关于 Haskell 而不是 Purescript,但更深入地解释了这个错误。)

要修复它,正如上面问题中所暗示的那样,您必须明确提及您要映射的值是 Left 值:

data Either a b = Left a | Right b

instance functorEither :: Functor (Either a)
  where
    map f (Right b) = Right $ f b
    map _ (Left a) = Left a

这很好,因为 Left a 可以在左侧解释为 Either a b 类型,在右侧解释为 Either a c.

至于实例“做什么”:“a b 不是仿函数,只有 eith a 是仿函数”是正确的 - 因为仿函数必须采用一种类型变量,Either a会,但 Either a b 不会。是的,因为在 Either a bEither a c 之间实际“变化”的类型变量是在 Right 中使用的类型变量,map 必须只映射到 Right 值,并单独保留 Left 个 - 这是唯一能满足所需类型的东西。

Either a b 通常被解释为表示计算结果,其中 Left 值表示失败,而 Right 值表示成功。从这个意义上说,它是 Maybe 的一个稍微“扩展”的版本——不同之处在于,失败不是由单个值 (Nothing) 表示,而是得到一段数据 (a 输入 Either a b) 它可以告诉您有关错误的信息。但是 Functor 实例与 Maybe 实例的工作方式相同:它映射任何成功,并单独留下失败。

(但是没有合乎逻辑的理由说明您 不能 “映射”Left 值。Bifunctor class 是 Functor 的扩展,它可以做到这一点。)