2016-06-24 47 views
5

Ho uno Future[Either[A, B]] e una funzione che fornisce un Future[C] da un B.Futuro [O [A, B]] a Futuro [O [A, C]] utilizzando una funzione (B => Futuro [C])

Devo trasformare lo Future[Either[A, B]] in Future[Either[A, C]].

C'è un modo diretto per ottenere il Future[Either[A, C]] e non uno Future[Either[A, Future[C]]]?

sto pensando a qualcosa di simile:

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) 

E 'solo pseudo-codice in quanto service.getValue(e.right) non compilare. Quale sarebbe il modo corretto di farlo?

risposta

7

Questo è ciò che mi si avvicinò con:

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(_)) 
} 

Fondamentalmente si può flatMap il futuro e poi se si tratta di una sinistra solo tornare al successo, se si tratta di un diritto è possibile applicare il futuro e mappa un Right ad esso.

3

Si può fare questo sollevando la funzione B => Future[C] in una funzione Either[E, B] => Future[Either[E, C]] e poi si può flatMap sul futuro originale.

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

Grazie. Deve solo essere restituito come un diritto: bToFC (r) .map (Right (_)) –

+0

@Alban: sì, modificato quello in – Daenyth

+0

Se si utilizza 'Future.successful' invece di' Future.apply' eviterete creando 'Left (e)' su un pool di thread. –

6

Ecco la soluzione scalaz. Si noti che utilizza la versione scalaz di entrambi.

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 

E 'fondamentalmente la stessa cosa @Ende Neu ha fatto, ma l'abbinamento e riconfezionamento è nascosto trasformatore monade.

+0

Non sarebbe più semplice per 'initial.flatMap (_. Fold (identità, (Right.apply _) componi f)'? – Daenyth

+0

Non viene compilato per me e non sono sicuro che funzioni, ma se sembra interessante e forse dovresti postarlo come risposta: –

+0

Hai ragione, il tipo è sbagliato, devi mappare 'Destra' sul risultato di' f' – Daenyth

1

Gatti ha attualmente an open pull request aggiungere un metodo per semiflatMapOptionT e XorT che prende una funzione B => F[C].

potremmo creare una funzione simile per scalaz.EitherT:

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]) 

allora si potrebbe fare:

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)