理解 Either 如何成为 Functor 的一个实例
在我的空闲时间我正在学习 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
现在:
我知道
fmap :: (c -> d) -> fc-> f d
如果我们将
f
替换为Either a
,我们会得到fmap :: (c -> d) ->要么 ac ->或者 a d
Right (gx)
的类型是要么是a (gx)
,而g x
的类型是d
,因此我们得到Right (gx)
的类型是Either a d
,这正是我们对fmap
的期望code> (参见上面的 2.)现在,如果我们查看
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:
I know that
fmap :: (c -> d) -> f c -> f d
if we substitute
f
withEither a
we getfmap :: (c -> d) -> Either a c -> Either a d
the type of
Right (g x)
isEither a (g x)
, and the type ofg x
isd
, so we have that the type ofRight (g x)
isEither a d
, which is what we expect fromfmap
(see 2. above)now, if we look at
Left (g x)
we can use the same reasoning to say that its type isEither (g x) b
, that isEither d b
, which is not what we expect fromfmap
(see 2. above): thed
should be the second parameter, not the first! So we can't map overLeft
.
Is my reasoning correct?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是对的。这种行为还有另一个非常重要的原因:您可以将
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 returnb
or fail with an error messagea
. (This is also, how the monad instance works). So it's only natural, that the functor instance won't touch theLeft
values, since you want to map over the computation, if it fails, there's nothing to manipulate.你的账户当然是对的。也许我们在处理这样的实例时遇到困难的原因是我们实际上一次定义了无限多个函子实例——每个可能的
Left
类型都有一个实例。但 Functor 实例是一种对系统中无限多种类型进行操作的系统方法。因此,我们定义了对系统中无限多种类型进行系统操作的无限多种方法。这个例子在两个方面涉及普遍性。不过,如果你分阶段来看,也许就不那么奇怪了。第一个类型是使用单元类型
()
及其唯一合法值()
的冗长版本的Maybe
:在这里我们可能会得到累了并进行抽象:
现在我们毫无问题地创建 Functor 实例,第一个看起来很像
Maybe
的实例:,再一次,为什么要麻烦,让我们进行抽象:
但是 a 项未受影响,因为
()
、String
和Int
值未受影响。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()
:Here we might get tired and make an abstraction:
Now we make our Functor instances, unproblematically, the first looking a lot like the instance for
Maybe
:But, again, why bother, let's make the abstraction:
The
Mere a
terms aren't touched, as the()
,String
andInt
values weren't touched.正如其他人提到的,
Either
类型在其两个参数中都是一个函子。但在 Haskell 中,我们能够(直接)在类型的最后一个参数中仅定义函子。在这种情况下,我们可以使用 newtype 来绕过限制:因此我们有构造函数
FlipEither :: Either ab -> FlipEither b a
将Either
包装到带有交换类型参数的newtype
中。我们有析构函数unFlipEither :: FlipEither ba ->要么是 b
将其解开。现在我们可以在FlipEither
的最后一个参数中定义一个函子实例,它实际上是Either
的第一个参数:请注意,如果我们忘记
FlipEither
有一段时间,我们只得到了Either
的Functor
定义,只是交换了Left
/Right
。现在,每当我们在Either
的第一个类型参数中需要Functor
实例时,我们都可以将该值包装到FlipEither
中,然后再将其解开。例如:更新:查看Data.Bifunctor,其中
Either
和(,)
是其实例。每个 bifunctor 有两个参数,并且每个参数都是一个函子。这反映在Bifunctor
的方法first
和second
中。Either
的Bifunctor
的定义非常对称: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 usingnewtype
s:So we have constructor
FlipEither :: Either a b -> FlipEither b a
that wraps anEither
into ournewtype
with swapped type arguments. And we have dectructorunFlipEither :: FlipEither b a -> Either a b
that unwraps it back. Now we can define a functor instance inFlipEither
's last argument, which is actuallyEither
's first argument:Notice that if we forget
FlipEither
for a while we get just the definition ofFunctor
forEither
, just withLeft
/Right
swapped. And now, whenever we need aFunctor
instance inEither
's first type argument, we can wrap the value intoFlipEither
and unwrap it afterward. For example: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 inBifunctor
's methodsfirst
andsecond
.The definition of
Bifunctor
ofEither
is very symetric:插在这里,它可能会有意义。
假设 a = String (错误消息)
您将 Either a 应用于 Float。
所以你有一个 f: Float ->整数例如四舍五入。
(任一字符串)(浮点型)= 任一字符串浮点型。
now (fmap f):: 要么字符串浮点 ->要么 字符串 Int
那么你打算用 f 做什么呢? f 不知道如何处理字符串,所以你不能在那里做任何事情。 显然您唯一可以采取的行动就是选择正确的值,同时保持左侧的值不变。
换句话说,a 是一个函子,因为有一个明显的 fmap 给出:
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: