Il existe des méthodes map
/flatMap
, il existe également des méthodes recover
/recoverWith
dans l'API standard Scala Future. Pourquoi il n'y a pas collectWith
?Qu'en est-il d'une méthode Scala Future # collectWith?
Le code de la méthode collect
est assez simple:
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))
}
Le code de la méthode collectWith
est alors facile d'imaginer:
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))
}
Je sais que je peux le mettre en œuvre et « étendre "l'API standard Future facilement grâce à cet article: http://debasishg.blogspot.fr/2008/02/why-i-like-scalas-lexically-scoped-open.html
Je l'ai fait dans mon projet:
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)
}
Peut-être que mes besoins pour cela ne justifient pas de l'implémenter dans l'API standard?
Voici pourquoi j'ai besoin de cette méthode dans mon projet 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)
}
Je ne sais pas comment faire sans ma méthode collectWith
.
Peut-être que ce n'est pas la bonne façon de faire quelque chose comme ça?
Connaissez-vous un meilleur moyen?
EDIT:
je peut-être une meilleure solution pour la méthode createCar
qui ne nécessite pas la méthode 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
}
Que pensez-vous de cette deuxième solution?
Deuxième édition:
Pour information, voici ma solution finale grâce à réponse @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)
}
}
Jules
Je trouve votre solution flatMap assez bien parce que lisible! Merci =) Et merci pour les informations sur la traduction d'une fonction partielle en fonction. Je ne connaissais pas cette astuce =) –