简体   繁体   English

如何将F [Either [A,B]]转换为Either [F [A],F [B]]

[英]How to transform F[Either[A, B]] to Either[F[A], F[B]]

A little bit of background: There is a .separate function in cats that allows me to get tuple (F[A], F[B]) out of F[Either[A, B]] . 背景知识:猫中有一个.separate函数,它使我可以从F[Either[A, B]]获取元组(F[A], F[B]) Given that, one can easily construct Either[F[A], F[B]] - given we can check F for emptiness (a monoid would do?). 鉴于此,可以很容易地构造Either[F[A], F[B]] -鉴于我们可以检查F的空性(类人动物会这样做吗?)。 The code for list could look like this 列表的代码可能如下所示

val l: List[Either[_, _]] = ???
l.separate match {
  case (Nil, rights) => Right(rights)
  case (lefts, _) => Left(lefts)
}

But this seems like a more general concept but I am not quite sure what that is. 但这似乎是一个更笼统的概念,但我不确定那是什么。 It looks kinda similar to .sequence but our G has two holes. 它看起来有点类似于.sequence但是我们的G有两个孔。 That is, we want a transformation F[G[A, B]] -> G[F[A], F[B]] . 也就是说,我们想要一个变换F[G[A, B]] -> G[F[A], F[B]]

Do you happen to know if such concept exists or how to achieve this goal with cats without pattern matching / if statements / Either.cond ? 您是否偶然知道这种概念是否存在,或者如何通过不带pattern matching / if statements / Either.cond猫来实现该目标?

You might try it with a combination of Alternative and Traverse . 您可以结合使用AlternativeTraverse来尝试。

This special case can be generalized from List to an arbitrary F : 这种特殊情况可以从List推广到任意F

def collectErrors[A, B](xs: List[Either[A, B]]): Either[List[A], List[B]] = {
  xs.traverse(_.left.map(List(_)).toValidated).toEither
}

Here is a shopping list of stuff that we need to make it work: 这是我们需要使其工作的东西的购物清单:

  1. We need some kind of replacement for List(_) . 我们需要某种替代List(_) That's usually Applicative[F].pure(_) (or _.pure[F] ), so we need Applicative . 通常是Applicative[F].pure(_) (或_.pure[F] ),因此我们需要Applicative
  2. We need a Monoid on F[X] so we can accumulate errors in the left part of Validated . 我们需要F[X]上的Monoid ,以便可以在Validated的左侧累积错误。 Fortunately, there is MonoidK[F[_]] , which knows how to generate a Monoid[F[X]] for any given X 幸运的是,有MonoidK[F[_]] ,它知道如何为任何给定X生成Monoid[F[X]]
  3. The F[_] must be traversable so we can get from F[Validated[F[A], B]] to Validated[F[A], F[B]] , thus we need Traverse . F[_]必须是可遍历的,因此我们可以从F[Validated[F[A], B]]Validated[F[A], F[B]] ,因此我们需要Traverse

There is a trait Alternative , which is a combination of Applicative and MonoidK . 有一个特质Alternative ,它是ApplicativeMonoidK的组合。 Putting it all together gives you: 将所有内容放在一起将为您提供:

  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
  }

This should now work for List , Vector , Chain , etc. 现在,这应该适用于ListVectorChain等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM