I believe that the answer you got uselessly mixed Either in the mix when Future already doing an excellent job with the error message. The main thing that you lacked is a way to get the value of a parameter from Option without explicitly throwing exceptions.
I suggest you change the Failure object to the following:
object Failures { sealed trait Failure extends Exception // Four types of possible failures here case object UserNotFound extends Failure case object NotAuthenticated extends Failure case object GoodNotFound extends Failure case object NoOwnership extends Failure // Put other errors here... // Converts options into Futures implicit class opt2future[A](opt: Option[A]) { def withFailure(f: Failure) = opt match { case None => Future.failed(f) case Some(x) => Future.successful(x) } } }
Now you can map Future[Option[A]] to Future[A] and specify a failure condition, as a result of which the following is understood for understanding:
def checkGood(user: User, good: Good) = if (checkOwnership(user, good)) Future.successful(good) else Future.failed(NoOwnership) val resultFuture: Future[JsResult] = for { userOpt <- userDao.findUser(userId) user <- userOpt.withFailure(UserNotFound) authOpt <- userDao.authenticate(user) auth <- authOpt.withFailure(NotAuthenticated) goodOpt <- goodRes.withFailure(GoodNotFound) checkedGood <- checkGood(user, good) } yield renderJson(Map("success" -> true))))
Now that you have Future[JsResult] , you can match the failed scenarios with the desired result, and the success scenario is just JsResult. I hope you use this in an asynchronous structure that expects you to feed it in the future and it has its own bad future for matching error responses (e.g. Play!).
source share