3

I have the result of a method: val res: Future[Int] Xor Future[String] = getResult(x)

and would like to transform it and use it as Future[Int Xor String]

I could not extrapolate my use case from the herding cats blog and am not sure whether a monad transformer would be the right tool here, perhaps rather some form of traverse?

Xor from cats stands in for any disjunction. Scalaz \/ or stdlib Either would be fine as well (though I would prefer a biased disjunction).

Thank you

kostja
  • 60,521
  • 48
  • 179
  • 224

2 Answers2

10

Just as sequence allows you to turn a F[G[A]] into a G[F[A]] when F has a Traverse instance and G is applicative, bisequence lets you turn a F[G[A], G[B]] into a G[F[A, B]] if F has a Bitraverse instance (and G is applicative).

Cats has provided a Bitraverse implementation for at least a couple of versions (I'm using 0.6.0-M2 here), so you can just write this:

import cats.data.Xor, cats.std.future._, cats.syntax.bitraverse._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def flip[A, B](x: Xor[Future[A], Future[B]]): Future[Xor[A, B]] = x.bisequence

Bitraverse is a little like Scalaz's Zip or Cozip (mentioned in the other answer), but it's more generic in that instances can be defined for any type constructor with two type arguments (assuming it has the appropriate semantics), not just tuples or disjunction.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • thanks Travis, works fine for me. I wish I would understand better what I am doing here :) – kostja May 19 '16 at 16:39
  • 1
    @kostja There are (almost) always people around to answer questions more interactively in the [Gitter room](https://gitter.im/typelevel/cats)! – Travis Brown May 19 '16 at 16:41
  • 1
    In this case the key is generalizing from `Traverse` and `sequence`, which are a little simpler and more widely applicable, so building up intuitions about how to use those is likely to help. – Travis Brown May 19 '16 at 16:43
  • could you please update the answer to cats 0.8.1? I assumed that importing `cats.instances.either._`, `cats.implicits._` & `cats.syntax.bitraverse._` would be enough, but even after importing `*.all._` `x.bisequence` refuses to compile with `value bisequence is not a member of Either[scala.concurrent.Future[Int],scala.concurrent.Future[Unit]]`. Scala 2.11.8 – kostja Dec 16 '16 at 15:01
  • @kostja Sure, but it's probably worth a new question (linking to this one), since there are a lot of differences between `Either` and `Xor` (and since a lot has changed in recent Cats versions). – Travis Brown Dec 16 '16 at 15:31
3

Scalaz has Functor.counzip, but there is no counzip in scalaz.syntax.functor so we need to call it on Functor[Future] directly :

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz.std.scalaFuture._
import scalaz.{\/, Functor}

val disj: Future[Int] \/ Future[String] = \/.right(Future.successful("foo"))
Functor[Future].counzip(disj)
// Future[Int \/ String] : Success(\/-(foo))

Scalaz also has a Cozip type class which gives you the inverse : F[A \/ B] => F[A] \/ F[B].

Peter Neyens
  • 9,770
  • 27
  • 33