【问题标题】:Scala Future, flatMap that works on EitherScala Future, flatMap 适用于 Either
【发布时间】:2015-01-05 17:12:57
【问题描述】:

真的有办法将 Future[Either[Future[T1], Future[T2]]] 类型的对象转换为 Either[Future[T1], Future[T2]] 类型的对象吗??

也许像 flatMap 这样的东西适用于 Either....

我正在尝试使这段代码工作(我有类似的代码可以实现包装的动作链,但它不涉及未来。它可以工作,更简单)。下面的代码就是在此基础上进行了必要的修改,使其适用于涉及期货的情况。

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

更新更新更新

基于来自 Didier 下面的 cmets (Scala Future, flatMap that works on Either)...这是一个有效的代码:

//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)
}

谢谢, 拉卡

【问题讨论】:

  • 其实我还是想走得更远一点,让其他要编写内部动作的程序员更容易......所以,InnerActionSig......,而不是Future[Option[ WebServResp]] => InnerActionOutType ...,我希望它是 Option[WebServResp] => InnerActionOutType ... 但是我猜现在的方式已经足够好了。

标签: scala future either flatmap


【解决方案1】:

Future[Either[Future[T1], Future[T2]]] 类型意味着有时稍后(那是未来)会得到一个 Either,所以到那时,你会知道计算会走哪条路,以及更晚会得到 T1 还是 T2 .

因此,选择哪个分支(LeftRight)的知识将在稍后公布。 Either[Future[T1], Future[T2] 类型意味着一个人现在拥有该知识(不知道结果将是什么,但已经知道它将是什么类型)。摆脱未来的唯一方法就是等待。

这里没有什么魔法,以后变成现在的唯一方法就是等待,在Future上用result完成,不推荐。 `

你可以做的就是说你对知道哪个分支不太感兴趣,只要它还没有完成,所以Future[Either[T1, T2]]就足够了。这很容易。假设你有 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)}
}

你还没有Either,但有未来,所以只是 flatMap

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

(需要一个隐式可用的ExecutionContext

【讨论】:

  • 嗨 Didier,我刚刚为我的原始问题提供了一些背景信息。我想我给这个问题的标题是不准确的。与您的评论相关,从我的街区我知道我的回报将永远留下。只是左派的内容是未来。这里棘手的部分,计算该内容的函数的输入也在未来......
  • 好吧,如果你真的知道 Either 是左派,这有点奇怪,但是然后把它扔到未来:future map {case Left(l) => l},如果你想让它成为左派,那么Left(future map {case Left(l) => l}
  • 好的,根据您的最新评论...所以我想如果我将 InnerActionOutType 从 Either[Future[Option[WebServResp]], Future[WebStatus]] 重新定义为 Future[Either[选项[WebServResp], WebStatus]]
  • 嗨,对不起,我刚刚阅读了您的补充评论....是的,我认为您是对的..让我试试看:stackoverflow.com/questions/26826190/…
【解决方案2】:

看起来您实际上不需要Either 的“失败”案例成为Future?在这种情况下,我们可以使用 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)

但最好始终将 Future 保留在 API 的外部,并将 flatMap 保留在代码中而不是客户端中。 (这里是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)
  }

(如果您需要“恢复”类型的操作,那么您确实需要将OWSR 设为Either,那么您仍应将InnerActionOutType 设为Future[Either[...]],并且您可以使用.traverse 或@987654333 @ 在您的操作中根据需要。如果您有“错误恢复”类型操作的示例,我可以在这里放一个示例)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-23
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 2021-01-18
    • 2016-10-27
    相关资源
    最近更新 更多