Haskell-适用于任一方

问题描述

我正在尝试理解Applicative和任一左派。来源:

80

我无法理解instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r 部分。这是没有道理的,因为:

Left e <*> _ = Left e

将返回Left (+3) <*> Right 5 ,同时:

Left (+3)

将返回Right (+1) <*> Left 3 。问题是不一致。为什么要这样做?如果我的问题不够清楚,我深表歉意。谢谢!

解决方法

TL; DR,这是一个故意的设计决定。

您应该将Right视为“默认”状态,并将Left视为“后备”状态。我确实想对您的上述声明做一些小的更正。 Left (+3) <*> Right 5不会像您所说的那样产生(+3),而是产生Left (+3)。这是一个重要的区别。第二个更正是Right (+1) <*> Left 3不是Left 4,而是Left 3。同样,这对于了解正在发生的事情很重要。

<*>运算符不能在Either上对称的原因是,LeftRight构造函数的类型不同。让我们看看专门针对<*>仿函数的Either的类型:

(<*>) :: Either a (b -> c) -> Either a b -> Either a c

请注意,只有第一个参数的Right面才需要是一个函数。这样,您就可以使用(<*>)将这样的参数链接在一起:

Right (+) <$> Right 3 <*> Right 2
> Right 5

但是如果第一个参数是Left 3

Right (+) <$> Left 3 <*> Right 2
> (Right (+) <$> Left 3) <*> Right 2
> Left 3 <*> Right 2
> Left 3

这也意味着,当(<*>)Left的类型不同时,通常可以使用Right。如果Left (+3) <*> Right 5应该产生Left 8,那么Left (++ "world") <*> Right 5应该产生什么,假设它们都可以被强制转换为同一类型,即Num a => Either (String -> String) a?当LeftRight不是同一类型时,不可能给出令人满意的答案,因为它们Either被限制为只能携带一种类型严重阻碍了实用程序。

这还允许您以某种方式将Left值视为例外。如果在任何阶段最终得到一个Left值,Haskell将停止执行计算,而只是将Left值一直向上级联。这也恰好与许多人对编程的思考方式相匹配。您可以想象为LeftRight值创建替代的计算集,但是在许多情况下,最终还是要用Left来填充id计算,因此这在实践中并没有太大的限制。如果要执行一对分支计算中的一个,则应使用常规分支语法(例如警卫队,模式或caseif语句),然后将值包装在{{1}中}。

,

考虑实例的以下等效定义:

instance Applicative (Either e) where
    pure = Right
    lhs <*> rhs = case lhs of
                      Right f -> fmap f rhs
                      otherwise -> lhs

如果lhs不是Right,则它必须是Left,因此我们将其原样返回。实际上,我们根本不需要与包装的值匹配。如果Right,我们将遵循Functor实例来查找返回的内容。

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

我再次给出一个定义,强调Left值的 content 无关紧要。如果第二个参数不是Right,则不必在其上显式匹配。它必须是Left,我们可以原样返回它。

,

如果您想知道Right … <*> Left …仍如何返回Left,那是因为在此定义中调用了fmap

instance Applicative (Either e) where
    pure          = Right
    Left  e <*> _ = Left e
    Right f <*> r = fmap f r

如果我们为fmap扩展Either的定义,那么<*>的定义如下:

Left  e <*> _ = Left e
Right f <*> r = case r of
  Left e -> Left e
  Right x -> Right (f x)

或者,更对称地写明所有情况:

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

我用下划线_标记了所丢弃的值。

请注意,当两个输入均为Right时,返回Right only 情况。实际上,这是可能唯一返回Right的时间。

在情况(4)中,我们只有一个Right (f :: a -> b)和一个Right (x :: a);我们没有e,因此我们无法返回Left,而获得b的唯一方法是将f应用于{{ 1}}。

在情况(1),(2)和(3)中,我们必须返回一个x,因为至少有一个输入是Left,所以我们缺少了{{1 }}或我们需要产生Left的{​​{1}}。

在情况(1)中,当两个输入均为a -> b时,a偏向第一个参数。

有一种类似于b的类型称为Left,它组合其“失败”情况,而不是选择其中一种,但受其限制:仅Either,而Either既是Validation又是Applicative

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...