[英]Haskell - Applicative upon Either's Left
I'm trying to understand Applicative and Either's Left.我正在尝试了解 Applicative 和任何一个都离开了。 Here is the source:
这是来源:
instance Applicative (Either e) where
pure = Right
Left e <*> _ = Left e
Right f <*> r = fmap f r
I'm unable to understand the Left e <*> _ = Left e
part.我无法理解
Left e <*> _ = Left e
部分。 It makes no sense because this:这是没有意义的,因为:
Left (+3) <*> Right 5
Would return Left (+3)
, while this:将返回
Left (+3)
,而这个:
Right (+1) <*> Left 3
would return Left 3
.将返回
Left 3
。 The problem is inconsistency.问题是不一致。 Why would the do this?
为什么要这样做? I apologize if my question isn't clean enough.
如果我的问题不够清楚,我深表歉意。 Thanks!
谢谢!
TL;DR, It's an intentional design decision. TL; DR,这是一个有意的设计决定。
You should think of Right
as the "default" state, and Left
as the "fallback" state.您应该将
Right
视为“默认”状态,将Left
视为“后备”状态。 I do want to make a small correction to your statements above.我确实想对你上面的陈述做一个小的更正。
Left (+3) <*> Right 5
does not produce (+3)
as you say, but rather Left (+3)
. Left (+3) <*> Right 5
不会像你说的那样产生(+3)
,而是产生Left (+3)
。 That's an important distinction.这是一个重要的区别。 The second correction is that
Right (+1) <*> Left 3
procues not Left 4
, but Left 3
.第二个更正是
Right (+1) <*> Left 3
不是Left 4
,而是Left 3
。 Again, this is important to understand what's going on.同样,这对于了解正在发生的事情很重要。
The reason why the <*>
operator cannot be symmetric over Either
is because the Left
and Right
constructors don't take the same type. <*>
运算符不能对称于Either
的原因是因为Left
和Right
构造函数不采用相同的类型。 Let's look at the type of <*>
specialized to the Either
functor:让我们看看专用于
Either
函子的<*>
类型:
(<*>) :: Either a (b -> c) -> Either a b -> Either a c
Notice how only the Right
side of the first argument is required to be a function.请注意如何只需要第一个参数的
Right
是一个函数。 This is so that you can use (<*>)
to chain together arguments like this:这样您就可以使用
(<*>)
将参数链接在一起,如下所示:
Right (+) <$> Right 3 <*> Right 2
> Right 5
But if the first argument were Left 3
:但是如果第一个参数是
Left 3
:
Right (+) <$> Left 3 <*> Right 2
> (Right (+) <$> Left 3) <*> Right 2
> Left 3 <*> Right 2
> Left 3
It also means that you can use (<*>)
in general when Left
and Right
don't have the same type.这也意味着当
Left
和Right
的类型不同时,您通常可以使用(<*>)
。 If Left (+3) <*> Right 5
should produce Left 8
, then what should Left (++ "world") <*> Right 5
produce, given that they can both be coerced to the same type, namely Num a => Either (String -> String) a
?如果
Left (+3) <*> Right 5
应该产生Left 8
,那么Left (++ "world") <*> Right 5
产生什么,因为它们都可以被强制为相同的类型,即Num a => Either (String -> String) a
? It's impossible to come up with a satisfactory answer that treats Left
and Right
equally when they aren't the same type, and a version of Either
that was restricted to carrying one type would have severely hampered utility.当
Left
和Right
不是同一种类型时,不可能想出一个令人满意的答案来平等对待它们,而限制为携带一种类型的Either
版本都会严重阻碍效用。
This also allows you to treat Left
values as exceptional in some way.这也允许您以某种方式将
Left
值视为异常值。 If at any stage, you end up with a Left
value, Haskell will stop performing calculations and just cascade the Left
value all the way up.如果在任何阶段,您最终得到一个
Left
值,Haskell 将停止执行计算并一直级联Left
值。 This also happens to match up well with the way a lot of people think about programming.这也恰好符合很多人对编程的看法。 You could imagine creating alternate sets of computations for
Left
and Right
values, but in many cases, you'd just end up filling the Left
computations with id
anyways, so this isn't too big a limitation in practice.您可以想象为
Left
和Right
值创建交替的计算集,但在许多情况下,您最终只会用id
填充Left
计算,因此这在实践中并没有太大的限制。 If you want to execute one of a pair of branching computations, you should use regular branching syntax, such as guards, patterns, or case
and if
statements and then wrap the values up in Either
at the end.如果要执行对分支的计算中的一个,你应该使用常规的分支的语法,如警卫,图案,或
case
和if
语句,然后包裹值高达中Either
在年底。
Consider this equivalent definition of the instance:考虑这个实例的等效定义:
instance Applicative (Either e) where
pure = Right
lhs <*> rhs = case lhs of
Right f -> fmap f rhs
otherwise -> lhs
If lhs
isn't a Right
, it must be a Left
, and so we return it as-is.如果
lhs
不是Right
,则它必须是Left
,因此我们按原样返回它。 We don't actually have to match against the wrapped value at all.我们实际上根本不需要匹配包装的值。 If it is a
Right
, we defer to the Functor
instance to find out what gets returned.如果它是
Right
,我们将Functor
实例来找出返回的内容。
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ l = l
Again, I've given a definition that emphasizes that the content of the Left
value doesn't matter.同样,我给出了一个定义,强调
Left
值的内容无关紧要。 If the second argument isn't a Right
, we don't have to explicitly match on it;如果第二个参数不是
Right
,我们不必显式匹配它; it must be a Left
, and we can just return it as-is.它必须是一个
Left
,我们可以按原样返回它。
If you're wondering how Right … <*> Left …
can still return a Left
, it's because of the fmap
call in this definition:如果您想知道
Right … <*> Left …
仍然可以返回Left
,那是因为此定义中的fmap
调用:
instance Applicative (Either e) where
pure = Right
Left e <*> _ = Left e
Right f <*> r = fmap f r
If we expand the definition of fmap
for Either
, then the definition of <*>
looks like this:如果我们扩大的定义
fmap
为Either
,那么的定义<*>
看起来像这样:
Left e <*> _ = Left e
Right f <*> r = case r of
Left e -> Left e
Right x -> Right (f x)
Or, written more symmetrically with all the cases spelled out explicitly:或者,用明确说明的所有情况更对称地书写:
Left e1 <*> Left _e2 = Left e1 -- 1
Left e <*> Right _x = Left e -- 2
Right _f <*> Left e = Left e -- 3
Right f <*> Right x = Right (f x) -- 4
I've marked with an underscore _
the values that are discarded.我用下划线标记
_
被丢弃的值。
Notice that the only case that returns Right
is when both inputs are Right
.请注意,返回
Right
的唯一情况是两个输入均为Right
。 In fact, that's the only time it's possible to return Right
.事实上,这是唯一一次可以返回
Right
。
In case (4) we only have a Right (f :: a -> b)
and a Right (x :: a)
;在情况 (4) 中,我们只有
Right (f :: a -> b)
和Right (x :: a)
; we don't have an e
, so we can't return a Left
, and the only way we have to obtain a b
is by applying f
to x
.我们没有
e
,所以我们不能返回Left
,我们必须获得b
的唯一方法是将f
应用于x
。
In cases (1), (2), and (3), we must return a Left
, because at least one of the inputs is Left
, so we are missing the a -> b
or the a
that we would need to produce a b
.在例(1),(2)和(3),我们必须返回一个
Left
,因为输入中的至少一个是Left
,所以我们缺少a -> b
或a
,我们将需要生产b
。
When both inputs are Left
in case (1), Either
is biased toward the first argument.当情况 (1) 中的两个输入都为
Left
时, Either
偏向于第一个参数。
There is a type similar to Either
called Validation
which combines its “failure” cases, instead of choosing one or the other, but it's more constrained: it's only an Applicative
, while Either
is both an Applicative
and a Monad
.有一种类似于
Either
称为Validation
的类型,它结合了它的“失败”情况,而不是选择一个或另一个,但它更受限制:它只是一个Applicative
,而Either
既是一个Applicative
又是一个Monad
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.