2017-01-15 3 views
3

Future[Either[_, _]을 반환하는 함수가 있다고 가정 해 보겠습니다.이 함수 중 일부는 오류가 발생했을 때 왼쪽에 적용한다는 의미입니다. 단순화 된 예는 다음과 같습니다.왼쪽에 flatMap을 남겼습니다

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 가장 가까운 것은 당신이 뭔가라는 leftFlatMap (에서 기대할 수 정확히 서명이 MonadErrorhandleError,하지만 당신은 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) 

또한 EitherThandleError 방법을 가지고 것처럼 보이게 MonadError에서 제공하는 구문을 사용할 수 있습니다, 그것은 조금에게로 스칼라 컴파일러를 얻기 위해 더 의식을 소요하지만, 당신의 작업은 오른쪽 모양이 인식 :

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) 

나는이 두 번째 버전을 선호하는 것,하지만 그것은 스타일과 취향의 문제입니다.