简体   繁体   English

Haskell - 适用于任何人的左边

[英]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的原因是因为LeftRight构造函数不采用相同的类型。 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.这也意味着当LeftRight的类型不同时,您通常可以使用(<*>) 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.LeftRight不是同一种类型时,不可能想出一个令人满意的答案来平等对待它们,而限制为携带一种类型的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.您可以想象为LeftRight值创建交替的计算集,但在许多情况下,您最终只会用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.如果要执行对分支的计算中的一个,你应该使用常规的分支的语法,如警卫,图案,或caseif语句,然后包裹值高达中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:如果我们扩大的定义fmapEither ,那么的定义<*>看起来像这样:

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 -> ba ,我们将需要生产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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM