2017-01-15 5 views
3

Предположим, у меня есть функции, которые возвращают Future[Either[_, _], и я хочу применить некоторые из этих функций в случае сбоев, то есть применять их только к левой стороне. Упрощенный пример:Left flatMap on EitherT

def operation1: Future[Either[String, Int]] = Future.successful(Right(5)) 
def operation2: Future[Either[String, Int]] = Future.successful(Left("error")) 
def operation2FallBackWork = Future.successful{ 
    println("Doing some revert stuff") 
    Left("Error happened, but reverting was successful") 
} 

val res = for { 
    res1 <- EitherT.fromEither(operation1) 
    res2 <- EitherT.fromEither(operation2)//.leftFlatMap(operation2FallBackWork) -???? 
} yield res1 + res2 

Await.result(res.toEither, 5 seconds) 

Как достичь этого?

ответ

10

Ближе всего к leftFlatMap является MonadError «s handleError, который имеет точно подпись можно было бы ожидать от чего-то под названием leftFlatMap (хотя заметим, что вам нужно изменить операцию резервного к EitherT и обеспечивают постоянную функцию вместо того, чтобы передавать его как есть). Вы можете использовать экземпляр EitherT непосредственно, как это:

import scala.concurrent.{ Await, Future } 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
import scalaz._, Scalaz._ 

def operation1: Future[Either[String, Int]] = Future.successful(Right(5)) 
def operation2: Future[Either[String, Int]] = Future.successful(Left("error")) 

def operation2FallBack: EitherT[Future, String, Int] = EitherT(
    Future.successful { 
    println("Doing some revert stuff") 
    "Error happened, but reverting was successful".left 
    } 
) 

val E: MonadError[({ type L[x] = EitherT[Future, String, x] })#L, String] = 
    implicitly 

val res = for { 
    a <- EitherT.fromEither(operation1) 
    b <- E.handleError(EitherT.fromEither(operation2))(_ => operation2FallBack) 
} yield a + b 

Await.result(res.toEither, 5.seconds) 

Вы также можете использовать синтаксис предоставленный MonadError, чтобы сделать его похожим EitherT имеет метод handleError, хотя это занимает немного больше церемонии, чтобы получить компилятор Scala для признать, что ваши операции имеют правильную форму:

import scala.concurrent.{ Await, Future } 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
import scalaz._, Scalaz._ 

type FE[x] = EitherT[Future, String, x] 

def operation1: FE[Int] = EitherT(Future.successful(5.right)) 
def operation2: FE[Int] = EitherT(Future.successful("error".left)) 

def operation2FallBack: FE[Int] = EitherT(
    Future.successful { 
    println("Doing some revert stuff") 
    "Error happened, but reverting was successful".left 
    } 
) 

val res = for { 
    a <- operation1 
    b <- operation2.handleError(_ => operation2FallBack) 
} yield a + b 

Await.result(res.toEither, 5.seconds) 

Я предпочитаю этот второй вариант, но это вопрос стиля и вкуса.

Смежные вопросы