如何将 F[Either[A, B]] 转换为 Either[F[A], F[B]]
How to transform F[Either[A, B]] to Either[F[A], F[B]]
一些背景知识:cats 中有一个 .separate
函数,它允许我从 F[Either[A, B]]
中获取元组 (F[A], F[B])
。鉴于此,可以轻松构造 Either[F[A], F[B]]
- 假设我们可以检查 F
是否为空(一个幺半群可以吗?)。列表的代码可能如下所示
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
的猫来实现这个目标?
您可以尝试结合使用 Alternative
和 Traverse
。
可以从 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
.
- 我们需要
F[X]
上的 Monoid
以便我们可以在 Validated
的左侧累积错误。幸运的是,有 MonoidK[F[_]]
,它知道如何为任何给定的 X
生成 Monoid[F[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
等
一些背景知识:cats 中有一个 .separate
函数,它允许我从 F[Either[A, B]]
中获取元组 (F[A], F[B])
。鉴于此,可以轻松构造 Either[F[A], F[B]]
- 假设我们可以检查 F
是否为空(一个幺半群可以吗?)。列表的代码可能如下所示
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
的猫来实现这个目标?
您可以尝试结合使用 Alternative
和 Traverse
。
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
. - 我们需要
F[X]
上的Monoid
以便我们可以在Validated
的左侧累积错误。幸运的是,有MonoidK[F[_]]
,它知道如何为任何给定的X
生成 F[_]
必须是可遍历的,所以我们可以从F[Validated[F[A], B]]
到Validated[F[A], F[B]]
,因此我们需要Traverse
。
Monoid[F[X]]
有一个特征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
等