2016-06-24 4 views
5

У меня есть Future[Either[A, B]] и функция, предоставляющая Future[C] от B.Будущее [Либо [A, B]] в будущее [Либо [A, C]], используя функцию (B => Будущее [C])

Мне нужно преобразовать Future[Either[A, B]] в Future[Either[A, C]].

Есть ли прямой способ получить Future[Either[A, C]], а не Future[Either[A, Future[C]]]?

Я думаю о чем-то вроде:

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) 

Это просто псевдо-код, так как service.getValue(e.right) не компилируется. Каков был бы правильный способ сделать это?

ответ

7

Это то, что я придумал:

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

В принципе вы можете flatMap будущее, а затем, если это левая просто возвращает успех, если это правильно, то вы можете подать заявление на будущее и карта a Right.

3

Вы можете сделать это, подняв функцию B => Future[C] в функцию Either[E, B] => Future[Either[E, C]], а затем вы можете flatMap в оригинальное будущее.

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

Спасибо. Он просто должен быть возвращен как правое: bToFC (r) .map (Right (_)) –

+0

@Alban: yep, отредактировано в – Daenyth

+0

Если вы используете 'Future.successful' вместо' Future.apply', вы избежите создавая 'Left (e)' в пуле потоков. –

6

Здесь находится решение scalaz. Обратите внимание, что он использует версию scalaz.

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 

Это в основном то же самое, что сделал @Ende Neu, но совпадение и rewrapping спрятана в монады трансформатора.

+0

Не было бы проще 'initial.flatMap (_. Fold (identity, (Right.apply _) compose f)'? – Daenyth

+0

Он не компилируется для меня, и я не уверен, что он может работать, но если это выглядит интересно и, возможно, вы должны отправить его в качестве ответа. –

+0

Вы правы, тип неправильный, вам нужно отобразить 'Right' в результате' f' – Daenyth

1

Кошки в настоящее время имеет an open pull request добавить метод semiflatMap к OptionT и XorT который принимает функцию B => F[C].

Мы могли бы создать аналогичную функцию для 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]) 

Тогда вы могли бы сделать:

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)