2016-06-24 14 views
5

Ich habe eine Future[Either[A, B]] und eine Funktion, die eine Future[C] von einer B bereitstellt.Zukunft [entweder [A, B]] bis Zukunft [Entweder [A, C]] mit einer (B => Future [C]) Funktion

Ich muss die Future[Either[A, B]] zu Future[Either[A, C]] umwandeln.

Gibt es einen direkten Weg, um die Future[Either[A, C]] und nicht eine Future[Either[A, Future[C]]] zu bekommen?

Ich denke an so etwas wie:

val eventuallyInitialValue: Future[Either[A, B]] = ??? 
val result: Future[Either[A, C]] = for { 
    e: Either[A, B] <- initialValue 
    c: C <- service.getValue(e.right) 
} yield e.right.map(_ => c) 

Es ist nur Pseudo-Code seit service.getValue(e.right) nicht kompiliert. Was wäre der richtige Weg, dies zu tun?

Antwort

7

Dies ist, was ich kam mit:

type B = Int 
type A = String 
type C = Long 
val eitherF: Future[Either[A, B]] = ??? 
def f(b: B): Future[C] = ??? 

val mapped: Future[Either[A, C]] = eitherF.flatMap { 
    case Left(a) => 
    Future.successful(Left(a)) 
    case Right(b) => 
    f(b).map(Right(_)) 
} 

Grundsätzlich können Sie die Zukunft flatMap und dann, wenn es eine linke ist nur der Erfolg zurückkehren, wenn es ein richtig können Sie die Zukunft und Karte beantragen a Right dazu.

3

Sie können dies tun, indem Sie die B => Future[C] Funktion in eine Either[E, B] => Future[Either[E, C]] Funktion heben und dann können Sie flatMap auf die ursprüngliche Zukunft.

eventuallyInitialValue.flatMap { 
    case Left(e) => Future.successful(Left(e)) 
    case Right(r) => bToFC(r).map(Right.apply _) 
} 
+0

Dank. Es muss nur als ein Recht zurückgegeben werden: bToFC (r) .map (Right (_)) –

+0

@Alban: yep, bearbeitet das in – Daenyth

+0

Wenn Sie 'Future.successful' anstelle von' Future.apply' verwenden, werden Sie vermeiden Erstellen von 'Left (e)' in einem Thread-Pool. –

6

Hier ist Scalaz Lösung. Beachten Sie, dass es die Scalaz-Version von beiden verwendet.

class A 
class B 
class C 

val initial: Future[A \/ B] = (new B).right.point[Future] 
val f: B => Future[C] = _ => (new C).point[Future] 

val result: Future[A \/ C] = (for { 
           e <- EitherT(initial) 
           res <- EitherT(f(e).map(_.right)) 
           } yield res).run 

Es ist im Grunde das gleiche, was @Ende Neu tat aber der passende und Transkodierung wird in Monade Transformator versteckt.

+0

Wäre es nicht einfacher zu 'initial.flatMap (_. Fold (Identität, (Right.apply _) compose f)'? – Daenyth

+0

Es kompiliert nicht für mich, und ich bin mir nicht sicher, ob es aber funktionieren kann es sieht interessant aus und vielleicht solltest du es als Antwort posten –

+0

Du hast Recht, der Typ stimmt nicht, du musst 'Richtig' über das Ergebnis von' f' abbilden – Daenyth

1

Katzen haben derzeit an open pull request ein Verfahren semiflatMap-OptionT und XorT hinzuzufügen, die B => F[C] eine Funktion übernimmt.

Wir konnten eine ähnliche Funktion für scalaz.EitherT erstellen:

import scalaz._, Scalaz._ 

def semiFlatMap[F[_]: Monad, A, B, C](e: EitherT[F, A, B])(f: B => F[C]): EitherT[F, A, C] = 
    e.flatMap(f andThen EitherT.right[F, A, C]) 

Dann könnten Sie tun:

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 

val futureEither: Future[Either[String, Int]] = Future.successful(Right(1)) 
val f: Int => Future[Long] = i => Future.successful(i + 1L) 

val appliedF: EitherT[Future,String,Long] = 
    semiFlatMap(EitherT.fromEither(futureEither))(f) 

val futureEither2: Future[Either[String, Long]] = appliedF.run.map(_.toEither)