将 Eithers 列表转换为包含列表的 Either
Converting a list of Eithers to an Either containing a list
我有一个函数,其中 return 是 Either
项的列表。如果它只包含 Right
个项目,我想 return 一个 Right
包含原始 Right
个项目中的值列表,但如果它包含任何 Left
项目 我想要一个 Left
包含原始 Left
项目中的值列表。所以如果我有:
[Right "a", Right "b", Right "c"]
它应该变成:
Right ["a", "b", "c"]
但如果我有:
[Right "a", Left "b", Right "c", Left "d"]
它应该变成:
Left ["b", "d"]
注意:Control.Monad 中的 sequence
函数不适用,因为它只保留第一个 Left
项。
我想到了这个:
eitherList :: [Either a1 a2] -> Either [a1] [a2]
eitherList l = if null lefts
then Right rights
else Left lefts
where f1 (Left a) (al, ar) = (a : al, ar)
f1 (Right a) (al, ar) = (al, a : ar)
(lefts, rights) = foldr f1 ([], []) l
但看起来很笨拙。有没有更简单或更好的方法来做到这一点?我刚开始学习仿函数和单子,我想知道它们是否相关?
大部分繁重的工作可以由 Data.Either.partitionEithers
完成:
eitherList :: [Either a1 a2] -> Either [a1] [a2]
eitherList l = case (partitionEithers l) of
([], rs) -> Right rs
(ls, _) -> Left ls
(partitionEithers
的实现与您为 fold 编写的内容非常相似。)
The sequence
function from Control.Monad
isn't suitable because it only keeps the first Left
item.
您的想法是正确的,只是缺少一些细节。有一个包,validation
, that offers a type called Validation
,它的形状就像 Either
:
data Validation err a
= Failure err
| Success a
但值得注意的是,它的 Applicative
实例可以如您所愿地工作,将“错误”值附加在一起:
instance (Semigroup err) => Applicative (Validation err) where
pure = Success
Failure e1 <*> Failure e2 = Failure (e1 <> e2)
Failure e1 <*> Success _ = Failure e1
Success _ <*> Failure e2 = Failure e2
Success f <*> Success a = Success (f a)
不像Either
,Validation
不能有Monad
实例,所以你需要使用更通用的sequenceA
(或traverse
)而不是 sequence
(或 mapM
):
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
Data.Validation
模块导出各种用于与 Either
相互转换的函数。为了您的目的,一个不错的选择是 liftError
:
liftError :: (b -> e) -> Either b a -> Validation e a
singleton :: a -> [a]
singleton = pure
liftError singleton :: Either e a -> Validation [e] a
全部都在这里:
import qualified Data.Validation as Validation
eitherList :: [Either e a] -> Either [e] [a]
eitherList = Validation.toEither . traverse (Validation.liftError pure)
-- Note: ‘traverse f’ = ‘sequenceA . fmap f’
并且您的示例按预期工作:
> example1 = [Right "a", Right "b", Right "c"]
> example2 = [Right "a", Left "b", Right "c", Left "d"]
> eitherList example1
Right ["a","b","c"]
> eitherList example2
Left ["b","d"]
我有一个函数,其中 return 是 Either
项的列表。如果它只包含 Right
个项目,我想 return 一个 Right
包含原始 Right
个项目中的值列表,但如果它包含任何 Left
项目 我想要一个 Left
包含原始 Left
项目中的值列表。所以如果我有:
[Right "a", Right "b", Right "c"]
它应该变成:
Right ["a", "b", "c"]
但如果我有:
[Right "a", Left "b", Right "c", Left "d"]
它应该变成:
Left ["b", "d"]
注意:Control.Monad 中的 sequence
函数不适用,因为它只保留第一个 Left
项。
我想到了这个:
eitherList :: [Either a1 a2] -> Either [a1] [a2]
eitherList l = if null lefts
then Right rights
else Left lefts
where f1 (Left a) (al, ar) = (a : al, ar)
f1 (Right a) (al, ar) = (al, a : ar)
(lefts, rights) = foldr f1 ([], []) l
但看起来很笨拙。有没有更简单或更好的方法来做到这一点?我刚开始学习仿函数和单子,我想知道它们是否相关?
大部分繁重的工作可以由 Data.Either.partitionEithers
完成:
eitherList :: [Either a1 a2] -> Either [a1] [a2]
eitherList l = case (partitionEithers l) of
([], rs) -> Right rs
(ls, _) -> Left ls
(partitionEithers
的实现与您为 fold 编写的内容非常相似。)
The
sequence
function fromControl.Monad
isn't suitable because it only keeps the firstLeft
item.
您的想法是正确的,只是缺少一些细节。有一个包,validation
, that offers a type called Validation
,它的形状就像 Either
:
data Validation err a
= Failure err
| Success a
但值得注意的是,它的 Applicative
实例可以如您所愿地工作,将“错误”值附加在一起:
instance (Semigroup err) => Applicative (Validation err) where
pure = Success
Failure e1 <*> Failure e2 = Failure (e1 <> e2)
Failure e1 <*> Success _ = Failure e1
Success _ <*> Failure e2 = Failure e2
Success f <*> Success a = Success (f a)
不像Either
,Validation
不能有Monad
实例,所以你需要使用更通用的sequenceA
(或traverse
)而不是 sequence
(或 mapM
):
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
Data.Validation
模块导出各种用于与 Either
相互转换的函数。为了您的目的,一个不错的选择是 liftError
:
liftError :: (b -> e) -> Either b a -> Validation e a
singleton :: a -> [a]
singleton = pure
liftError singleton :: Either e a -> Validation [e] a
全部都在这里:
import qualified Data.Validation as Validation
eitherList :: [Either e a] -> Either [e] [a]
eitherList = Validation.toEither . traverse (Validation.liftError pure)
-- Note: ‘traverse f’ = ‘sequenceA . fmap f’
并且您的示例按预期工作:
> example1 = [Right "a", Right "b", Right "c"]
> example2 = [Right "a", Left "b", Right "c", Left "d"]
> eitherList example1
Right ["a","b","c"]
> eitherList example2
Left ["b","d"]