Haskell - 适用于左派
Haskell - Applicative upon Either's Left
我正在尝试了解 Applicative 和 Either's Left。这是来源:
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
会returnLeft (+3)
,而这个:
Right (+1) <*> Left 3
会returnLeft 3
。问题是不一致。他们为什么要这样做?如果我的问题不够干净,我深表歉意。谢谢!
TL;DR,这是一个有意的设计决定。
您应该将 Right
视为“默认”状态,将 Left
视为“后备”状态。我确实想对你上面的陈述做一个小的更正。 Left (+3) <*> Right 5
不会像你说的那样产生 (+3)
,而是产生 Left (+3)
。这是一个重要的区别。第二个更正是 Right (+1) <*> Left 3
不是 Left 4
,而是 Left 3
。同样,这对于了解正在发生的事情很重要。
<*>
运算符不能在 Either
上对称的原因是因为 Left
和 Right
构造函数不采用相同的类型。让我们看一下专门用于 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
?当 Left
和 Right
不是同一类型时,不可能得出一个令人满意的答案,并且 Either
的版本仅限于携带一种类型严重阻碍效用。
这还允许您以某种方式将 Left
值视为异常值。如果在任何阶段,您最终得到一个 Left
值,Haskell 将停止执行计算并直接将 Left
值一直向上级联。这也恰好与许多人思考编程的方式相吻合。您可以想象为 Left
和 Right
值创建备用计算集,但在许多情况下,您最终只会用 id
填充 Left
计算,所以这在实践中并不是太大的限制。如果你想执行一对分支计算中的一个,你应该使用常规分支语法,例如守卫、模式或 case
和 if
语句,然后将值包装在 Either
最后。
考虑实例的这个等效定义:
instance Applicative (Either e) where
pure = Right
lhs <*> rhs = case lhs of
Right f -> fmap f rhs
otherwise -> lhs
如果 lhs
不是 Right
,它一定是 Left
,所以我们 return 它 as-is。我们实际上根本不需要匹配包装值。如果它 是 一个 Right
,我们将推迟到 Functor
实例以找出得到 returned.
的内容
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ l = l
同样,我给出了一个定义,强调 Left
值的 content 无关紧要。如果第二个参数不是 Right
,我们不必显式匹配它;它必须是 Left
,我们可以 return 它 as-is.
如果你想知道Right … <*> Left …
怎么还能return一个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
我用下划线标记了 _
被丢弃的值。
请注意 仅 情况下 returns Right
是两个输入均为 Right
的情况。事实上,这是唯一一次 可能 到 return Right
。
情况(4)我们只有一个Right (f :: a -> b)
和一个Right (x :: a)
;我们没有 e
,所以我们不能 return Left
,我们必须获得 b
的唯一方法是应用 f
到 x
.
在情况(1)、(2)、(3)中,我们必须return一个Left
,因为至少有一个输入是Left
,所以我们缺少我们需要生成 b
.
的 a -> b
或 a
当两个输入都是Left
时(1),Either
偏向第一个参数。
有一种类似于 Either
的类型称为 Validation
,它 结合了 它的“失败”案例,而不是选择一个或另一个,但它是更受限制:它只是一个 Applicative
,而 Either
既是 Applicative
又是 Monad
.
我正在尝试了解 Applicative 和 Either's Left。这是来源:
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
会returnLeft (+3)
,而这个:
Right (+1) <*> Left 3
会returnLeft 3
。问题是不一致。他们为什么要这样做?如果我的问题不够干净,我深表歉意。谢谢!
TL;DR,这是一个有意的设计决定。
您应该将 Right
视为“默认”状态,将 Left
视为“后备”状态。我确实想对你上面的陈述做一个小的更正。 Left (+3) <*> Right 5
不会像你说的那样产生 (+3)
,而是产生 Left (+3)
。这是一个重要的区别。第二个更正是 Right (+1) <*> Left 3
不是 Left 4
,而是 Left 3
。同样,这对于了解正在发生的事情很重要。
<*>
运算符不能在 Either
上对称的原因是因为 Left
和 Right
构造函数不采用相同的类型。让我们看一下专门用于 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
?当 Left
和 Right
不是同一类型时,不可能得出一个令人满意的答案,并且 Either
的版本仅限于携带一种类型严重阻碍效用。
这还允许您以某种方式将 Left
值视为异常值。如果在任何阶段,您最终得到一个 Left
值,Haskell 将停止执行计算并直接将 Left
值一直向上级联。这也恰好与许多人思考编程的方式相吻合。您可以想象为 Left
和 Right
值创建备用计算集,但在许多情况下,您最终只会用 id
填充 Left
计算,所以这在实践中并不是太大的限制。如果你想执行一对分支计算中的一个,你应该使用常规分支语法,例如守卫、模式或 case
和 if
语句,然后将值包装在 Either
最后。
考虑实例的这个等效定义:
instance Applicative (Either e) where
pure = Right
lhs <*> rhs = case lhs of
Right f -> fmap f rhs
otherwise -> lhs
如果 lhs
不是 Right
,它一定是 Left
,所以我们 return 它 as-is。我们实际上根本不需要匹配包装值。如果它 是 一个 Right
,我们将推迟到 Functor
实例以找出得到 returned.
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ l = l
同样,我给出了一个定义,强调 Left
值的 content 无关紧要。如果第二个参数不是 Right
,我们不必显式匹配它;它必须是 Left
,我们可以 return 它 as-is.
如果你想知道Right … <*> Left …
怎么还能return一个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
我用下划线标记了 _
被丢弃的值。
请注意 仅 情况下 returns Right
是两个输入均为 Right
的情况。事实上,这是唯一一次 可能 到 return Right
。
情况(4)我们只有一个Right (f :: a -> b)
和一个Right (x :: a)
;我们没有 e
,所以我们不能 return Left
,我们必须获得 b
的唯一方法是应用 f
到 x
.
在情况(1)、(2)、(3)中,我们必须return一个Left
,因为至少有一个输入是Left
,所以我们缺少我们需要生成 b
.
a -> b
或 a
当两个输入都是Left
时(1),Either
偏向第一个参数。
有一种类似于 Either
的类型称为 Validation
,它 结合了 它的“失败”案例,而不是选择一个或另一个,但它是更受限制:它只是一个 Applicative
,而 Either
既是 Applicative
又是 Monad
.