2017-09-28 7 views
0

Quelle est la manière idiomatique dans Scala de taper une API qui peut renvoyer ou non des résultats? Je peux simplement taper Futur [Option [A]] et retourner un Aucun s'il n'y a pas de résultats, mais cela rendra les choses un peu plus verbeuses pour les clients API parce qu'il doit faire un appariement de formes (ou d'autres techniques comme pliage) sur l'option retournée. Toutefois, les contrats à terme nous donnent déjà un moyen de dire qu'il n'y a pas de résultats qui font échouer. Mais je ne sais pas si c'est une façon correcte ou expressive de dire que l'appel ne renvoie pas de résultats.Utilisez Future.successful (None) vs Future.failed (new Exception()) pour une API qui peut ne renvoyer aucun résultat?

trait SomeAPI { 
    def fetch(): Future[String] 

} 


class NoResultsException extends Exception 

object APIImpl extends SomeAPI { 

    def asyncDBCall(): Future[List[String]] = ??? 

    override def fetch(): Future[String] = asyncDBCall().map(r => if (r.isEmpty) throw new NoResultsException() else r.head) 
} 

Au début, il semble que les deux options sont acceptables et la décision finale peut être juste une question de préférence personnelle, mais peut-être que je me manque quelque chose et faire un avenir n'est pas une bonne option.

+1

[Reactivemongo] (http://reactivemongo.org/releases/0.12/documentation/tutorial /find-documents.html) renvoie 'Future [Option [T]]'. Je suppose que ce serait Scala idiomatique, au lieu de «null» utiliser «Option», «Soit» ou tout ce qui vous convient. –

Répondre

4

Les exceptions doivent être réservées aux erreurs. Si l'API renvoie ou non des résultats et que les deux cas sont considérés comme normaux, l'API doit utiliser Option.

Si cela le rend plus verbeux en forçant le client à gérer les deux situations, c'est bien, car le client devrait gérer les deux situations. Les exceptions sont bonnes lorsque vous ne voulez pas forcer le code client à gérer un cas exceptionnel (erreur inattendue) qui devrait probablement se répercuter en cascade et être géré par un gestionnaire d'erreurs. Maintenant, si l'absence de résultats indique vraiment que quelque chose s'est mal passé, alors une exception serait appropriée.

1

Je m'attendrais à ce que les clients veulent traiter le «succès, aucun résultat» et l'échec différemment, en général (par exemple, réessayer dans le second cas, mais pas le premier). Si vous notez le premier avec un futur échoué, vous faites de la verbosité pour les clients pire, pas mieux: ils ont juste besoin de vérifier l'exception au lieu du succès, et l'API ne les aide pas à être conscient de cela comme Future[Option[A]] aurait.

Si vous vous souciez vraiment de la verbosité, vous pouvez ajouter des extracteurs personnalisés ou des méthodes que les clients peuvent utiliser s'ils le souhaitent, par ex.

implicit class FutureOption[A](future: Future[Option[A]]) { 
    def mapOpt[B](handleSuccessWithResult: A => B, handleSuccessWithNoResult:() => B) = Future.map { 
    case Some(x) => onSuccessWithResult(x) 
    case None => onSuccessWithNoResult() 
    } 
} 
0

Les autres ont raison. Les échecs ne sont pas un bon moyen de décrire l'absence de résultat concret, si cela fait partie du fonctionnement normal.

Scala vous donne une autre possibilité de noter explicitement, qu'il peut y avoir aucun résultat, traits scellé.

sealed trait MyOperationReturn 
case object OkNoResult 
case class OkWithStringResponse(data: String) extends MyOperationReturn 
case class OkWithIntResponse(data: Int) extends MyOperationReturn 

L'utilisateur peut alors simplement faire correspondre le motif. Cette option est particulièrement pratique si vous avez plusieurs types de retour (pas seulement deux) et aucun type de retour vide naturel (comme Seq.empty)