Scala Future#collectWith方法怎么样?

时间:2016-02-09 18:44:28

标签: scala future standard-library

还有map / flatMap方法,还有Scala Future标准API中的recover / recoverWith方法。

为什么没有collectWith

collect方法的代码非常简单:

def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] =
  map {
    r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
  }

collectWith方法的代码很容易想象:

def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
  flatMap {
    r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
  }

我知道我可以实施它并且"扩展"这篇文章很容易理解Future标准API:http://debasishg.blogspot.fr/2008/02/why-i-like-scalas-lexically-scoped-open.html

我在我的项目中做到了这一点:

class RichFuture[T](future: Future[T]) {
  def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
    future.flatMap {
      r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
    }
}

trait WithRichFuture {
  implicit def enrichFuture[T](person: Future[T]): RichFuture[T] = new RichFuture(person)
}

也许我对此的需求不足以在标准API中实现它?

这就是我在Play2项目中需要这个方法的原因:

def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
  def handleResponse: PartialFunction[WSResponse, Future[Car]] = {
    case response: WSResponse if response.status == Status.CREATED => Future.successful(response.json.as[Car])
    case response: WSResponse
        if response.status == Status.BAD_REQUEST && response.json.as[Error].error == "not_the_good_one" =>
          createCar(key, eligibleCars.tail)
  }

  // The "carApiClient.createCar" method just returns the result of the WS API call.
  carApiClient.createCar(key, eligibleCars.head).collectWith(handleResponse)
}

如果没有我的collectWith方法,我不知道如何做到这一点。

也许不是这样做的正确方法?
你知道更好的方法吗?

修改

对于不需要createCar方法的collectWith方法,我可能有更好的解决方案:

def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
  for {
    mayCar: Option[Car] <- Future.successful(eligibleCars.headOption)
    r: WSResponse <- carApiClient.createCar(key, mayCar.get) if mayCar.nonEmpty
    createdCar: Car <- Future.successful(r.json.as[Car]) if r.status == Status.CREATED
    createdCar: Car <- createCar(key, eligibleCars.tail) if r.status == Status.BAD_REQUEST && r.json.as[Error].error == "not_the_good_one"
  } yield createdCar
}

您如何看待第二种解决方案?

第二次修改:

仅供参考,感谢@Dylan回答:

,这是我的最终解决方案
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {

  def doCall(head: Car, tail: List[Car]): Future[Car] = {
    carApiClient
      .createCar(key, head)
      .flatMap( response =>
        response.status match {
          case Status.CREATED => Future.successful(response.json.as[Car])
          case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
            createCar(key, tail)
        }
      )
  }

  eligibleCars match {
    case head :: tail => doCall(head, tail)
    case Nil => Future.failed(new RuntimeException)
  }

}

1 个答案:

答案 0 :(得分:2)

怎么样:

<INC>\".+\"$|"<".+">"$ {
           /* NUL-terminate the filename by overwriting the trailing "*/
           yytext[yyleng - 1] = '\0';
           newfile(yytext + 1);
           BEGIN INITIAL;
         }

两个变化:

  • def createCar(key: String, eligibleCars: List[Car]): Future[Car] = { def handleResponse(response: WSResponse): Future[Car] = response.status match { case Status.Created => Future.successful(response.json.as[Car]) case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" => createCar(key, eligibleCars.tail) case _ => // just fallback to a failed future rather than having a `collectWith` Future.failed(new NoSuchElementException("your error here")) } // using flatMap since `handleResponse` is now a regular function carApiClient.createCar(key, eligibleCars.head).flatMap(handleResponse) } 不再是部分功能。 handleResponse会返回失败的未来,这实际上就是您在自定义case _实施中所做的事情。
  • 使用collectWith代替flatMap,因为collectWith现在适合该方法签名

编辑额外信息

如果您确实需要支持handleResponse方法,则可以通过调用部分函数上的PartialFunction始终将PartialFunction[A, Future[B]]转换为Function[A, Future[B]],例如

orElse

这样做可以让您调整现有的部分功能以适应val handleResponsePF: PartialFunction[WSResponse, Future[Car]] = { case .... } val handleResponse: Function[WSResponse, Future[Car]] = handleResponsePF orElse { case _ => Future.failed(new NoSucheElementException("default case")) } 来电。

(好的,从技术上讲,它已经存在了,但你会抛出MatchErrors而不是你自己的自定义异常)