简体   繁体   English

Scala Future,适用于Either的flatMap

[英]Scala Future, flatMap that works on Either

Is there really a way to transform an object of type Future[Either[Future[T1], Future[T2]]] to and object of type Either[Future[T1], Future[T2]] ?? 有没有办法将Future类型的对象[未来[T1],未来[T2]]]转换为类型为[Future [T1],Future [T2]]的对象?

Maybe something like flatMap that works on Either.... 也许像flatMap这样适用于Either ....

I'm trying to make this code work (I have similar code that implements wrapped chain-of actions, but it doesn't involve future. It works, much simpler). 我正在尝试使这个代码工作(我有类似的代码实现包裹的行动链,但它不涉及未来。它工作,更简单)。 The code below is based on that, with necessary modification to make it work for situation that involves futures. 下面的代码是基于此,通过必要的修改使其适用于涉及期货的情况。

case class WebServResp(msg: String)
case class WebStatus(code: Int)
type InnerActionOutType = Either[Future[Option[WebServResp]], Future[WebStatus]]
type InnerActionSig = Future[Option[WebServResp]] => Either[Future[Option[WebServResp]], Future[WebStatus]]

val chainOfActions: InnerActionSig = Seq(
  {prevRespOptFut => 
    println("in action 1: " + prevRespOptFut)
    //dont care about prev result
    Left(Future.successful(Some(WebServResp("result from 1"))))
  },
  {prevRespOptFut => 
    println("in action 2: " + prevFutopt)
    prevRespOptFut.map {prevRespOpt =>
      //i know prevResp contains instance of WebServResp. so i skip the opt-matching
      val prevWebServResp = prevRespOpt.get
      Left(Some(prevWebServResp.msg + " & " + " additional result from 2"))
    }

    //But the outcome of the map above is: Future[Left(...)]
    //What I want is Left(Future[...])
  }
)

type WrappedActionSig = InnerActionOutType => InnerActionOutType 
val wrappedChainOfActions = chainOfActions.map {innerAction => 
  val wrappedAction: WrappedActionSig = {respFromPrevWrappedAction =>
    respFromPrevWrappedAction match {
      case Left(wsRespOptFut) => {        
        innerAction(wsRespOptFut)       
      }
      case Right(wsStatusFut) => {
        respFromPrevWrappedAction
      }
    }
  }
  wrappedAction
}

wrappedChainOfActions.fold(identity[WrappedActionIOType] _)  ((l, r) => l andThen r).apply(Left(None))

UPDATE UPDATE UPDATE 更新更新更新

Based on comments from Didier below ( Scala Future, flatMap that works on Either )... here's a code that works: 根据下面Didier的评论( Scala Future,适用于Either的flatMap )......这是一个有效的代码:

//API
case class WebRespString(str: String)
case class WebStatus(code: Int, str: String)
type InnerActionOutType = Either[Future[Option[WebRespString]], Future[WebStatus]]
type InnerActionSig = Future[Option[WebRespString]] => InnerActionOutType

type WrappedActionSig = InnerActionOutType => InnerActionOutType
def executeChainOfActions(chainOfActions: Seq[InnerActionSig]): Future[WebStatus] = {
  val wrappedChainOfActions : Seq[WrappedActionSig] = chainOfActions.map {innerAction => 
    val wrappedAction: WrappedActionSig = {respFromPrevWrappedAction =>
      respFromPrevWrappedAction match {
        case Left(wsRespOptFut) => {        
          innerAction(wsRespOptFut)       }
        case Right(wsStatusFut) => {
          respFromPrevWrappedAction
        }
      }
    }
    wrappedAction
  }  

  val finalResultPossibilities = wrappedChainOfActions.fold(identity[InnerActionOutType] _)  ((l, r) => l andThen r).apply(Left(Future.successful(None)))
  finalResultPossibilities match {
    case Left(webRespStringOptFut) => webRespStringOptFut.map {webRespStringOpt => WebStatus(200, webRespStringOpt.get.str)}
    case Right(webStatusFut) => webStatusFut
  }  
}

//API-USER

executeChainOfActions(Seq(
  {prevRespOptFut => 
    println("in action 1: " + prevRespOptFut)
    //dont care about prev result
    Left(Future.successful(Some(WebRespString("result from 1"))))
  },
  {prevRespOptFut => 
    println("in action 2: " + prevRespOptFut)
    Left(prevRespOptFut.map {prevRespOpt => 
      val prevWebRespString = prevRespOpt.get
      Some(WebRespString(prevWebRespString.str + " & " + " additional result from 2"))
    })
  }  
)).map {webStatus =>
  println(webStatus.code + ":" + webStatus.str)
}

executeChainOfActions(Seq(
  {prevRespOptFut => 
    println("in action 1: " + prevRespOptFut)
    //Let's short-circuit here
    Right(Future.successful(WebStatus(404, "resource non-existent")))
  },
  {prevRespOptFut => 
    println("in action 2: " + prevRespOptFut)
    Left(prevRespOptFut.map {prevRespOpt => 
      val prevWebRespString = prevRespOpt.get
      Some(WebRespString(prevWebRespString.str + " & " + " additional result from 2"))
    })
  }  
)).map {webStatus =>
  println(webStatus.code + ":" + webStatus.str)
}

Thanks, Raka 谢谢,拉卡

The type Future[Either[Future[T1], Future[T2]]] means that sometimes later (that's future) one gets an Either, so at that time, one will know which way the calculation will go, and whether one will, still later, gets a T1 or a T2. 类型Future[Either[Future[T1], Future[T2]]]意味着有时候后来(那是未来)得到一个Either,所以在那个时候,人们会知道计算的方向,以及是否会,还是以后,获得T1或T2。

So the knowledge of which branch will be chosen ( Left or Right ) will come later. 因此,选择哪个分支( LeftRight )的知识将在稍后出现。 The type Either[Future[T1], Future[T2] means that one has that knowledge now (don't know what the result will be, but knows already what type it will be). 类型Either[Future[T1], Future[T2]意味着现在有一个知识(不知道结果是什么,但知道它将是什么类型)。 The only way to get out of the Future is to wait. 走出未来的唯一方法就是等待。

No magic here, the only way for later to become now is to wait, which is done with result on the Future, and not recommended. 这里没有魔法,以后成为现在的唯一方法就是等待,这是通过未来的result完成的,而不是推荐的。 ` `

What you can do instead is say that you are not too interested in knowing which branch is taken, as long has it has not completed, so Future[Either[T1, T2]] is good enough. 相反,你可以做的就是说你不太了解哪个分支被采用,只要还没有完成,所以Future[Either[T1, T2]]就够了。 That is easy. 这很容易。 Say you have the Either and you would rather not look not but wait for the actual result : 假设你有Either,你宁愿不看,但等待实际结果:

def asFuture[T1, T2](
    either: Either[Future[T1], Future[T2]])(
    implicit ec: ExecutionContext)
 : Future[Either[T1, T2] =  either match {
   case Left(ft1) => ft1 map {t1 => Left(t1)}
   case Right(ft2) => ft2 map {t2 => Right(t2)}
}

You don't have the Either yet, but a future on that, so just flatMap 你还没有Either ,但是还有未来,所以只有flatMap

f.flatMap(asFuture) : Future[Either[T1, T2]]

(will need an ExecutionContext implicitly available) (需要隐式提供ExecutionContext

It seems like you don't actually need the "failure" case of the Either to be a Future ? 看起来你实际上并不需要Either的“失败”案例成为Future In which case we can use scalaz (note that the "success" case of an either should be on the right): 在这种情况下,我们可以使用scalaz(请注意,两者的“成功”案例应该在右侧):

import scalaz._
import scalaz.Scalaz._

def futureEitherFutureToFuture[A, B](f: Future[Either[A, Future[B]]])(
  implicit ec: ExecutionContext): Future[Either[A, B]] =
  f.flatMap(_.sequence)

But it's probably best to always keep the Future on the outside in your API, and to flatMap in your code rather than in the client. 但最好始终将Future保留在API的外部,并在代码中而不是在客户端中使用flatMap (Here it's part of the foldLeftM ): (这里是foldLeftM的一部分):

case class WebServResp(msg: String)
case class WebStatus(code: Int)
type OWSR = Option[WebServResp]
type InnerActionOutType = Future[Either[WebStatus, OWSR]]
type InnerActionSig = OWSR => InnerActionOutType

def executeChain(chain: List[InnerActionSig]): InnerActionOutType = 
  chain.foldLeftM(None: OWSR) {
    (prevResp, action) => action(prevResp)
  }

//if you want that same API
def executeChainOfActions(chainOfActions: Seq[InnerActionSig]) =
  executeChain(chainOfActions.toList).map {
    case Left(webStatus) => webStatus
    case Right(webRespStringOpt) => WebStatus(200, webRespStringOpt.get.str)
  }

(If you need "recovery" type actions, so you really need OWSR to be an Either , then you should still make InnerActionOutType a Future[Either[...]] , and you can use .traverse or .sequence in your actions as necessary. If you have an example of an "error-recovery" type action, I can put an example of that here) (如果你需要“恢复”类型的动作,所以你真的需要OWSR成为一个Either ,那么你仍然应该让InnerActionOutType成为一个Future[Either[...]] ,你可以在你的动作中使用.traverse.sequence如果你有一个“错误恢复”类型动作的例子,我可以在这里举一个例子)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM