理解 Either 如何成为 Functor 的一个实例

发布于 2024-10-20 11:27:34 字数 1165 浏览 1 评论 0原文

在我的空闲时间我正在学习 Haskell,所以这是一个初学者问题。

在我的阅读中,我遇到了一个示例,说明如何将 Either a 制作为 Functor 的实例:

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

现在,我试图理解为什么在 a 的情况下实现映射Right 值构造函数,但在 Left 的情况下却不是?

这是我的理解:

首先让我将上面的实例重写为

instance Functor (Either a) where
    fmap g (Right x) = Right (g x)
    fmap g (Left x) = Left x

现在:

  1. 我知道 fmap :: (c -> d) -> fc-> f d

  2. 如果我们将 f 替换为 Either a,我们会得到 fmap :: (c -> d) ->要么 ac ->或者 a d

  3. Right (gx)的类型是要么是a (gx),而g x的类型是d,因此我们得到 Right (gx) 的类型是 Either a d,这正是我们对 fmap 的期望code> (参见上面的 2.)

  4. 现在,如果我们查看 Left (gx),我们可以使用相同的推理来判断它的类型是 Either (gx) b,即 Either d b,这不是我们对 fmap 的期望(参见上面的 2.):d 应该是第二个参数,而不是第一个!因此我们无法映射 Left

我的推理正确吗?

In my free time I'm learning Haskell, so this is a beginner question.

In my readings I came across an example illustrating how Either a is made an instance of Functor:

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

Now, I'm trying to understand why the implementation maps in the case of a Right value constructor, but doesn't in the case of a Left?

Here is my understanding:

First let me rewrite the above instance as

instance Functor (Either a) where
    fmap g (Right x) = Right (g x)
    fmap g (Left x) = Left x

Now:

  1. I know that fmap :: (c -> d) -> f c -> f d

  2. if we substitute f with Either a we get fmap :: (c -> d) -> Either a c -> Either a d

  3. the type of Right (g x) is Either a (g x), and the type of g x is d, so we have that the type of Right (g x) is Either a d, which is what we expect from fmap (see 2. above)

  4. now, if we look at Left (g x) we can use the same reasoning to say that its type is Either (g x) b, that is Either d b, which is not what we expect from fmap (see 2. above): the d should be the second parameter, not the first! So we can't map over Left.

Is my reasoning correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆蝶 2024-10-27 11:27:34

这是对的。这种行为还有另一个非常重要的原因:您可以将 Either a b 视为计算,它可能成功并返回 b 或失败并显示错误消息 一个。 (这也是 monad 实例的工作原理)。因此,函子实例不会触及 Left 值是很自然的,因为您想要映射计算,如果失败,则没有任何可操作的。

This is right. There is also another quite important reason for this behavior: You can think of Either a b as a computation, that may succeed and return b or fail with an error message a. (This is also, how the monad instance works). So it's only natural, that the functor instance won't touch the Left values, since you want to map over the computation, if it fails, there's nothing to manipulate.

梦罢 2024-10-27 11:27:34

你的账户当然是对的。也许我们在处理这样的实例时遇到困难的原因是我们实际上一次定义了无限多个函子实例——每个可能的 Left 类型都有一个实例。但 Functor 实例是一种对系统中无限多种类型进行操作的系统方法。因此,我们定义了对系统中无限多种类型进行系统操作的无限多种方法。这个例子在两个方面涉及普遍性。

不过,如果你分阶段来看,也许就不那么奇怪了。第一个类型是使用单元类型 () 及其唯一合法值 () 的冗长版本的 Maybe

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

在这里我们可能会得到累了并进行抽象:

data Unless a b    = Mere a     | Genuine b

现在我们毫无问题地创建 Functor 实例,第一个看起来很像 Maybe 的实例:

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

,再一次,为什么要麻烦,让我们进行抽象:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

但是 a 项未受影响,因为 ()StringInt 值未受影响。

Your account is right of course. Maybe the reason why we have a difficulty with instances like this is that we are really defining infinitely many functor instances at once -- one for each possible Left type. But a Functor instance is a systematic way of operating on the infinitely many types in the system. So we are defining infinitely many ways of systematically operating on the infinitely many types in the system. The instance involves generality in two ways.

If you take it by stages, though, maybe it's not so strange. The first of these types is a longwinded version of Maybe using the unit type () and its only legitimate value ():

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

Here we might get tired and make an abstraction:

data Unless a b    = Mere a     | Genuine b

Now we make our Functor instances, unproblematically, the first looking a lot like the instance for Maybe:

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

But, again, why bother, let's make the abstraction:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

The Mere a terms aren't touched, as the (), String and Int values weren't touched.

み格子的夏天 2024-10-27 11:27:34

正如其他人提到的,Either 类型在其两个参数中都是一个函子。但在 Haskell 中,我们能够(直接)在类型的最后一个参数中仅定义函子。在这种情况下,我们可以使用 newtype 来绕过限制:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

因此我们有构造函数 FlipEither :: Either ab -> FlipEither b aEither 包装到带有交换类型参数的 newtype 中。我们有析构函数 unFlipEither :: FlipEither ba ->要么是 b 将其解开。现在我们可以在 FlipEither 的最后一个参数中定义一个函子实例,它实际上是 Either 的第一个参数:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

请注意,如果我们忘记 FlipEither有一段时间,我们只得到了 EitherFunctor 定义,只是交换了 Left/Right 。现在,每当我们在 Either 的第一个类型参数中需要 Functor 实例时,我们都可以将该值包装到 FlipEither 中,然后再将其解开。例如:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

更新:查看Data.Bifunctor,其中 Either(,) 是其实例。每个 bifunctor 有两个参数,并且每个参数都是一个函子。这反映在 Bifunctor 的方法 firstsecond 中。

EitherBifunctor 的定义非常对称:

instance Bifunctor Either where
    bimap f _ (Left a)  = Left (f a)
    bimap _ g (Right b) = Right (g b)

    first  f = bimap f id

    second f = bimap id f

As others mentioned, Either type is a functor in its both arguments. But in Haskell we are able to (directly) define only functors in a type's last arguments. In cases like this, we can get around the limitation by using newtypes:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

So we have constructor FlipEither :: Either a b -> FlipEither b a that wraps an Either into our newtype with swapped type arguments. And we have dectructor unFlipEither :: FlipEither b a -> Either a b that unwraps it back. Now we can define a functor instance in FlipEither's last argument, which is actually Either's first argument:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

Notice that if we forget FlipEither for a while we get just the definition of Functor for Either, just with Left/Right swapped. And now, whenever we need a Functor instance in Either's first type argument, we can wrap the value into FlipEither and unwrap it afterward. For example:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

Update: Have a look at Data.Bifunctor, of which Either and (,) are instances of. Each bifunctor has two arguments and is a functor in each of them. This is reflected in Bifunctor's methods first and second.

The definition of Bifunctor of Either is very symetric:

instance Bifunctor Either where
    bimap f _ (Left a)  = Left (f a)
    bimap _ g (Right b) = Right (g b)

    first  f = bimap f id

    second f = bimap id f
二货你真萌 2024-10-27 11:27:34

现在,我试图理解为什么
实施图的情况下
正确的值构造函数,但不是
如果是左派?

插在这里,它可能会有意义。

假设 a = String (错误消息)
您将 Either a 应用于 Float。

所以你有一个 f: Float ->整数例如四舍五入。

(任一字符串)(浮点型)= 任一字符串浮点型。

now (fmap f):: 要么字符串浮点 ->要么 字符串 Int
那么你打算用 f 做什么呢? f 不知道如何处理字符串,所以你不能在那里做任何事情。 显然您唯一可以采取的行动就是选择正确的值,同时保持左侧的值不变。

换句话说,a 是一个函子,因为有一个明显的 fmap 给出:

  • 对于 Right 值,应用 f
  • 对于 Left 值,不执行任何操作

Now, I'm trying to understand why the
implementation maps in the case of a
Right value constructor, but doesn't
in the case of a Left?

Plug in here and it might make sense.

Assume a = String (an error message)
You apply Either a to an Float.

So you have an f: Float -> Integer say for example roundoff.

(Either String) (Float) = Either String Float.

now (fmap f):: Either String Float -> Either String Int
So what are you going to do with f? f doesn't have a clue what to do with strings so you can't do anything there. That is obviously the only thing you can act on are the right values while leaving the left values unchanged.

In other words Either a is a functor because there is such an obvious fmap given by:

  • for Right values apply f
  • for Left values do nothing
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文