背景知识:猫中有一个.separate
函数,它使我可以从(F[A], F[B])
中提取元组F[Either[A, B]]
。鉴于此,我们可以轻松构造Either[F[A], F[B]]
-假设我们可以检查F
是否为空(一个类人动物会做什么?)。 list的代码可能看起来像这样
val l: List[Either[_, _]] = ???
l.separate match {
case (Nil, rights) => Right(rights)
case (lefts, _) => Left(lefts)
}
但是,这似乎是一个更笼统的概念,但我不确定那是什么。它看起来与.sequence
类似,但我们的G
有两个孔。也就是说,我们需要一个转换F[G[A, B]] -> G[F[A], F[B]]
。
您碰巧知道这种概念是否存在,或者如何在没有pattern matching / if statements / Either.cond
的情况下养猫来实现这个目标?
答案 0 :(得分:3)
您可以结合使用Alternative
和Traverse
进行尝试。
This special case可以从List
推广到任意F
:
def collectErrors[A, B](xs: List[Either[A, B]]): Either[List[A], List[B]] = {
xs.traverse(_.left.map(List(_)).toValidated).toEither
}
以下是我们要使其正常工作所需的购物清单:
List(_)
的替代品。通常是Applicative[F].pure(_)
(或_.pure[F]
),因此我们需要Applicative
。Monoid
上使用F[X]
,以便我们可以在Validated
的左侧累积错误。幸运的是,这里有MonoidK[F[_]]
,它知道如何为任何给定的Monoid[F[X]]
X
F[_]
必须是可遍历的,这样我们才能从F[Validated[F[A], B]]
到Validated[F[A], F[B]]
,因此我们需要Traverse
。有一个特征Alternative
,它是Applicative
和MonoidK
的组合。
全部放在一起可以给您:
import scala.util.Either
import cats._
import cats.syntax.either._
import cats.syntax.traverse._
import cats.syntax.applicative._
def collectErrors[F[_]: Alternative : Traverse, X, Y](xs: F[Either[X, Y]])
: Either[F[X], F[Y]] = {
implicit val mon = MonoidK[F].algebra[X]
xs.traverse(_.left.map(_.pure[F]).toValidated).toEither
}
这现在应该适用于List
,Vector
,Chain
等