Cats, reducing a Seq of EitherT

Viewed 747

I am working on a project where I discovered the cats's EitherT type. In one of my tasks I would like to reduce a Seq of of producer of EitherT[Future, L, R] to an EitherT[Future, L, Seq[R]] when they are all right. And to abort the reduction and return the failed LeftT[Future, L, Seq[R]] otherwise.

I have a successful reduction for the happy path with the following code:

type Producer = () => EitherT[Future, L, Seq[R]] // where L and R are known types.
private def execute(producers:Seq[Producer]):EitherT[Future, L, Seq[R]] = {
  producers match {
    case last :: Nil =>
      last()
    case current :: nexts =>
      current().flatMap(res => execute(nexts).map(res ++ _))
   } 
}

But I cannot find a clean way to handle a LefT. Do you have any tips on how to handle the left case on my reduction ?

Thanks

Edit: because it may not be clear enough.

My input is a Seq[Producer] (A Producer give an EitherT when it is applied). I want to apply and reduce my producers to one EitherT but I have to abort the process if one producer fail (returns one EitherT which hold a Left).

Given :

  • P1, P2 and P3, three producers that returns a Right EitherT, respectively Seq(A), Seq(B) and Seq(C, D)
  • Reduction of Seq(P1, P2, P3) will return an EitherT of a Right(Seq(A, B, C, D)).

Given:

  • P1 and P3, two producers that returns a Right EitherT, respectively Seq(A) and Seq(C, D)
  • P2 one producer returning a `Left("fail")

  • Reduction of Seq(P1, P2, P3) will return an EitherT of a Left("fail").

Edit 2: Another possibility is to fold the producers bu the result looks more or less the same (and the accumulation of passed producers is less clean) :

var history = Seq.empty[]
val empty = //..
producers.foldLeft(empty) {
  case (left, producer) =>
    history :+ producer // May be in history while failed
    producer().flatMap(i => left.map(_ + i))
}
1 Answers

Use .sequence

  import cats.implicits._

  val success = List(
    EitherT.right[Int](Option("1")),
    EitherT.right[Int](Option("2")))

  println(success.sequence) // EitherT(Some(Right(List(1, 2))))

  val error = success :+ EitherT.left[String](Option(3))

  println(error.sequence) // EitherT(Some(Left(1)))
Related