I have an use case where I need to use for comprehension with ADT in Scala. I could write the same code using flatMaps but it seems a bit unreadable. Below is the piece of code.
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}")
}
}
In the above code, the next method is invoked on a resultant ADT content and result of if statement. How do I include the if statement in the for-comprehension to achieve the same.
Thanks in advance.
You can put pattern matching and if
statements in a for comprehension on Future
:
for {
MovieBookingInformationFetched(m) <- future1
if m.availableSeats > 0
MovieBookingUpdated(updatedMovieBooking) <- future2(m)
} yield updatedMovieBooking
However, this will be translated into Future.filter
, so if the predicate is not satisfied, or the pattern cannot be matched, you'll end up with
Future.failed(new NoSuchElementException("Future.filter predicate was not satisfied")
You can then catch this failure in a recover
statement, after the for-comp. The problem is that you want to catch three different "errors": the fact that there are no more available seats, the fact that fetchRecordByImdbAndScreenId
can return a MovieBookingInformationFetchError
, and the fact that updateSeatReservationByImdbAndScreenId
can return a MovieBookingUpdateFailed
.
You won't be able to dissociate between these three using only for comprehension, unless you define custom exceptions, instead of custom result types (and recover these exceptions afterwards).