2017-06-04 3 views
0

J'ai un cas d'utilisation où je dois utiliser pour la compréhension avec ADT dans Scala. Je pourrais écrire le même code en utilisant flatMaps mais il semble un peu illisible. Voici le morceau de code.ADT avec pour la compréhension

case class MovieRecord(movie: Movie, 
         screenId: String, 
         availableSeats: Int, 
         reservedSeats: Option[Int] = None) { 
    def movieInfo = new MovieInfoResponse(movie.imdbId, screenId, movie.title, availableSeats, reservedSeats.getOrElse(0)) 
} 


sealed trait MovieBookingInformation 
case class MovieBookingInformationFetched(bookMovie: MovieRecord) extends MovieBookingInformation 
case object MovieBookingInformationFetchError extends MovieBookingInformation 


def modifyBooking(reserveMovie: MovieSelected): Future[String] = { 
fetchRecordByImdbAndScreenId(reserveMovie.imdbId, reserveMovie.screenId) flatMap { 
    case MovieBookingInformationFetched(m) if (m.availableSeats > 0) => 
     updateSeatReservationByImdbAndScreenId(m.copy(availableSeats = m.availableSeats - 1, reservedSeats = Some(m.reservedSeats.getOrElse(0) + 1))) flatMap { 
     case MovieBookingUpdated(updatedMovieBooking) => 
      Future.successful(s"One seat reserved at Screen - ${updatedMovieBooking.screenId}") 
     case MovieBookingUpdateFailed => 
      Future.successful(s"Movie seat reservation failed at screen ${reserveMovie.screenId}") 
     } 
    case MovieBookingInformationFetched(m) => 
     Future.successful(s"Sorry! No more seats available for ${m.movie.title} at Screen - ${m.screenId}") 
    case MovieBookingInformationFetchError => Future.successful(s"No movie with IMDB ID ${reserveMovie.imdbId} found at ${reserveMovie.screenId}") 
    } 
} 

Dans le code ci-dessus, la méthode suivante est invoquée sur un contenu ADT résultant et le résultat de l'instruction if. Comment puis-je inclure l'instruction if dans la for-comprehension pour atteindre la même chose.

Merci d'avance.

Répondre

1

Vous pouvez mettre pattern matching et if déclarations dans une de compréhension sur Future:

for { 
    MovieBookingInformationFetched(m) <- future1 
    if m.availableSeats > 0 
    MovieBookingUpdated(updatedMovieBooking) <- future2(m) 
} yield updatedMovieBooking 

Cependant, ce sera traduit en Future.filter, donc si le prédicat est pas satisfait, ou le modèle ne peut pas être adapté, vous vous retrouverez avec

Future.failed(new NoSuchElementException("Future.filter predicate was not satisfied") 

vous pouvez alors prendre cet échec dans une déclaration recover, après la for-comp. Le problème est que vous voulez attraper trois "erreurs" différentes: le fait qu'il n'y a plus de sièges disponibles, le fait que fetchRecordByImdbAndScreenId peut retourner un MovieBookingInformationFetchError, et le fait que updateSeatReservationByImdbAndScreenId peut retourner un MovieBookingUpdateFailed.

Vous ne serez pas en mesure de dissocier entre ces trois en utilisant uniquement pour la compréhension, sauf si vous définissez des exceptions personnalisées, au lieu de types de résultats personnalisés (et récupérer ces exceptions par la suite).

+0

Merci @Cyrille Corpet! Très appréciée! Je pense que je suis bon à partir maintenant ... Est-ce bon d'utiliser la carte à plat que j'ai utilisée dans cette situation ou je devrais plutôt opter pour la compréhension à la place? Merci encore – boseAbhishek

+0

Personnellement, je pense que vous devriez toujours utiliser un for-comprehension, à moins que tout ce que vous voulez faire est _map_ ou _flatMap_. Dès que vous les combinez, éventuellement avec _filter_, il devient très difficile à comprendre du point de vue du lecteur suivant. Par conséquent, dans de tels cas, utilisez pour la compréhension. Gardez à l'esprit, bien sûr, que la première phase du compilateur convertit immédiatement les compréhensions en expressions map/flatMap/filter - elles sont donc entièrement équivalentes. – Phasmid

+0

Personnellement, j'écrirais ce que vous mettez dans votre flatmap comme une fonction d'aide, et faites un pour la compréhension –