Most of the time my Future[T] operations are dependent to previous future in the chain. I am using flatMap function with pattern matching most of the time. Such as;
findUser(userId).flatMap {
case None => Future.successful(NotFound("No user with given id"))
case Some(user) => findAddress(user.addressId).flatMap {
case None => Future.successful(NotFound("No address with given id"))
case Some(address) => findCity(address.cityId).flatMap {
case None => Future.successful(NotFound("No city with given id"))
case Some => Future.successful(Ok)
}
}
}
with this way i am able to return an object related with the problem, all branchings are being handled. But downside of this approach in my opinion (and my code reading pleasure) it is getting nested a lot. Also if the lines are too long it is impossible to track which case statement is which even with a proper formatting. So that goes to the right-bottom side of the editor.
The other way one would suggest might using for comprehension. Below is kind of a equivalent of the code above. But the difference is for-comp one is throwing an exception if the if-guard is not satisfied. Also it returns an option to use which wherever i want to use i need to call get method (which i don't want to do);
val items = for {
user <- findUser(userId) if user.isDefined
address <- findAddress(user.addressId) if address.isDefined
city <- findCity(address.cityId) if address.isDefined
} yield (user.get, address.get, city.get)
Again one may suggest catching the exception but as i read from many sources catching exceptions are considered not good. Also the exception wouldn't provide which case statement didn't satisfy the condition.
Same thing applies for return statements as well. As myself coming from java and .net based languages, i am inclined to use the style below.
val user = Await.result(findUser(userId), timeout)
if (user.isEmpty) {
return Future.successful(NotFound("No user with given id"))
}
val address = Await.result(findAddress(user.get.addressId), timeout)
if (address.isEmpty) {
return Future.successful(NotFound("No address with given id"))
}
val city = Await.result(findUser(address.get.cityId), timeout)
if(city.isEmpty) {
return Future.successful(NotFound("No city with given id"))
}
Future.successful(Ok)
which is definitely out of question in my understanding. First of all it makes the code-block blocking, secondly again it forces me to use get values and uses return blocks which are similar with the throwing exceptions in the matter of cutting the execution short.
Haven't been able to find an elegant solution to this. I am currently going with the nested approach which makes it harder to read
Thanks
You should use .failed futures rather than successful to communicate exceptional conditions:
sealed trait NotFoundErr
class NoUser extends Exception("No user with given id") with NotFoundErr
class NoAddress extends Exception("No address with given id") with NotFoundErr
class NoCity extends Exception("No city with given id") with NotFoundErr
def getOrElse[T](ifNot: Exception)(what: => Future[Option[T]]) = what
.map(_.getOrElse(throw ifNot))
val items = for {
user <- getOrElse(new NoUser)(findUser(userId))
address <- getOrElse(new NoAddress)(findAddress(user.addressId))
city <- getOrElse(new NoCity)(findCity(address.cityId))
} yield (user, address, city)
items
.map(_ => Ok)
.recover { case e: Exception with NotFoundErr => NotFound(e.getMessage) }
You can make it look even fancier with an implicit:
object RichFuture {
implicit class Pimped[T](val f: Future[Option[T]]) extends AnyVal {
def orElse(what: => T) = f.map(_.getOrElse(what))
}
}
Now, you can write the for-comprehension like:
for {
user <- findUser(userId) orElse throw(new NoUser)
address <- findAddress(user.addressId) orElse throw(new NoAddress)
city <- findCity(address.cityId) orElse throw(new NoCity)
} yield (user, address, city)
The elegant solution to this problem is to use an appropriate data type to wrap the different failure cases.
I'd suggest you look into
Cats Validated or
Scalaz Validation
Those types collects the operation outcome and compose well in comprehensions and possibly with futures
Related
The following block of code fails to build with error :
value flatMap is not a member of Product with Serializable
[error] if (matchingUser.isDefined) {
Here's the code:
for {
matchingUser <- userDao.findOneByEmail(email)
user <- {
if (matchingUser.isDefined) {
matchingUser.map(u => {
// update u with new values...
userDao.save(u)
u
})
} else {
val newUser = new User(email)
userDao.create(newUser)
newUser
}
}
} yield user
Method userDao.findOneByEmail(email) returns anFuture[Option[User]]object.
My Google searches are only aboutEitherwithRightandLeft` types.
Maybe I'm not doing this the proper way, please teach me how to properly do this.
The first branch of the if statement returns Option[User], the other one returns User. So, the result of the entire statement is inferred to have type Product with Serializable because it is the only common supertype of the two.
You could wrap the last statement inside the if into an Option (just do Option(newUser) instead of newUser) or, better yet, use fold instead of the whole if(matchingUser.isDefined) {...} thingy:
matchingUser.fold {
val u = new User(email)
userDao.create(u)
u
} { u =>
userDao.save(u)
u
}
This will make the result of that statement to be Option[User] as you probably intended ... but it still will not compile.
The problem is that you cannot mix different types of monads in the for-comprehension: since the first one was Future, all the others have to be as well. You can't have an Option there.
How to get around that? Well, one possibility is to make userDao.create and userDao.save return a future of the object they just saved. That is, probably a better thing to do in general, then what you have, because now you are returning the user before it was actually stored ... What if the create operation fails afterwards? Then you can just rewrite your for-comprehension like this:
for {
matchingUser <- userDao.findOneByEmail(email)
user <- matchingUser.fold(userDao.create(new User(email)))(userDao.save)
} yield user
Or just get rid of it entirely (for-comprehension is an overkill for simple cases like this):
userDao
.findOneByEmail(email)
.flatMap(_.fold(usrDao.create(new User(email)))(userDao.save))
Or, it may look a little nicer with pattern matching instead of fold in this case:
userDao
.findOneByEmail(email)
.flatMap {
case Some(u) => userDao.save(u)
case None => userDao.create(new User(email))
}
Here is my test example to reproduce your issue with solution.
Basically your user returns wrapper around Future (Option of Future), but it expected to be Future, as the first statement in for-comprehension.
That is why I've applied some addition unwrapping. See sample below.
Note: it does not looks so nice, I'd prefer to rewrite it with flatMap map.
object T {
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def future1: Future[Option[Int]] = ???
def future2(i: Int): Future[Double] = ???
for {
matchingUser <- future1
user <- {
if (matchingUser.isDefined) {
matchingUser.map { i =>
future2(i)
}
} else {
Some(future2(42))
}
} match {
case None => Future.successful(-42.0)
case Some(x) => x
}
} yield user
}
And the same implemented with flatMap:
val userFuture = future1.flatMap {
case Some(i) => future2(i)
case None => future2(42)
}
I am new to Play Framework using Scala. I want to evaluate a condition and in that condition evaluates to true, I want to send a response and exit at that point. However, the code below that I am trying continues till the end.
I tried breaking with a return statement - However, I get a type mismatch. Can someone help me out with this?
def hello = Action { request =>
if (true) {
Ok("in If")
// Return at this point
}
print("This line should not be printed")
Ok("final")
}
EDIT
Assume a GET call is being made with 4 parameters - name, age, married, spouse. I want to make sure all 3 params (name, age, married) are passed in, and if married is true, check if spouse is passed in. If this validation fails, I want to respond saying Bad Request. Else, continue with logic. How do I write this?
Here is an alternative way to do it:
case class QueryInput(name: String, age: Int, married: Boolean, spouse: Option[String]) {
def validated = if(married && spouse.isEmpty) None else Some(this)
}
def validateInput(request: RequestHeader) = {
val input = for {
name <- request.queryString.get("name").flatMap(_.headOption)
age <- request.queryString.get("age").flatMap(_.headOption.flatMap(a=>Try(a.toInt).toOption))
married <- request.queryString.get("married").flatMap(_.headOption.map(_=="true"))
} yield {
QueryInput(name, age, married, request.queryString.get("spouse").flatMap(_.headOption))
}
input.flatMap(_.validated)
}
def hello() = Action { request =>
validateInput(request) match {
case Some(input) => Ok(input.toString())
case None => BadRequest
}
}
In fact, there are many options. You could also play with the Either class to do validation: Left value to accumulate errors and return bad request, right value to construct your validated input.
My recommendation would be to have a method for validating the parameters. Then do a simple if/else to check if the parameters are valid and return a success or a general error.
If you really want a specific
First thing:
When the block evaluates, all of its expressions and declarations are processed in order, and then the block returns the value of the last expression as its own value.
Second: don't use return.
And the third one is a Play Framework way of resolving your problem: action composition. Though I would not say that it is trivial.
You can do this, by putting a return Ok in but really, thats not the scala way. What you want to do is to change your mindset and imagine everything as a function. If you didnt know, if-then-else always returns a value. For example, you can actually write if this way:
def hello = Action { request =>
val result = if (true) {
Ok("foo")
} else {
Ok("bar")
}
result
}
of course, an even more scala way is to use matchers
def hello = Action { request =>
val result = true match {
case true => Ok("foo")
case _ => Ok("bar")
}
result
}
Take that one step further and you dont even need to specify the result object at all, because scala figures out the returning object based on the last object returned/created.
def hello = Action { request =>
true match {
case true => Ok("foo")
case _ => Ok("bar")
}
}
EDIT: TO answer the OP's edit, you still want to use the matcher. Assuming your vals are options, heres what you do:
def hello(nameOpt:Option[String], ageOpt:Option[String], marriedOpt:Option[String]) = Action { request =>
(nameOpt, ageOpt, marriedOpt) match {
case (Some(name), Some(age), Some(married)) => Ok("all 3 are given")
case (Some(name), Some(age), _) => Ok("all 2 are given")
// functionally same as above
// case (Some(name), Some(age), None) => Ok("all 2 are given")
// some combination of the above
case (None, None, Some(married)) => Ok("married but no name or age")
// default case
case _ => Ok("bar")
}
}
this thread gave me an idea how to structure my code: Scala-way to handle conditions in for-comprehensions?
The part in question:
// First create the JSON
val resultFuture: Future[Either[Failure, JsResult]] = for {
userRes <- userDao.findUser(userId)
user <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)
auth <- authRes.withFailure(NotAuthenticated).right
goodRes <- goodDao.findGood(goodId)
good <- goodRes.withFailure(GoodNotFound).right
checkedGood <- checkGood(user, good).right
} yield renderJson(Map("success" -> true)))
This are the lines I do not understand:
user <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)
The userRes.withFailure(UserNotFound).right is mapped to userDao.authenticate(user). This will create a new Either with a Future on its right, correct?
How can
val resultFuture: Future[Either[Failure, JsResult]]
be of its type. I think instead of a JsResult there should be another future.
Can anyone explain this to me?
EDIT: Since cmbaxter and Arne Claassen confirmed this, the new question is: How should I write this code, so it does not look ugly, but clean and structured?
I believe the answer you received needlessly mixed Either's into the mix when Future's are already perfectly capable of communicating failure. The main thing you were missing was a way to get from an Option to the option's value without explicitly throwing exceptions.
I would suggest that you change the Failures 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 a Future[Option[A]] to a Future[A] and specify the failure condition, resulting in a for comprehension like this:
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 a Future[JsResult] you can map the failed scenarios to your desired output and the success scenario is just the JsResult. Hopefully you are using this in an asynchronous framework which expects you to feed it a future and has its own failed future to error response mapping (such as Play!).
I am trying to find the best way to handle jedis commands from scala. I am trying to implement a finally block, and prevent the java exceptions from bubbling up to my caller.
Does the following code make sense, and is it the best I can do performance wise, if I want to ensure that I handle exceptions when redis may be down temporarily? This trait would be extended by an object, and I'd call objectname.del(key). I feel like I'm combining too many concepts (Either, Option, Try, feels like there should be a cleaner way)
trait MyExperiment {
implicit class TryOps[T](val t: Try[T]) {
def eventually[Ignore](effect: => Ignore): Try[T] = {
val ignoring = (_: Any) => { effect; t }
t transform (ignoring, ignoring)
}
}
val jpool:JedisPool = initialize()
// init the pool at object creation
private def initialize(): JedisPool =
{
val poolConfig = new JedisPoolConfig()
poolConfig.setMaxIdle(10)
poolConfig.setMinIdle(2)
poolConfig.setTestWhileIdle(true)
poolConfig.setTestOnBorrow(true)
poolConfig.setTestOnReturn(true)
poolConfig.setNumTestsPerEvictionRun(10)
new JedisPool( poolConfig , "localhost" )
}
// get a resource from pool. This can throw an error if redis is
// down
def getFromPool: Either[Throwable,Jedis] =
Try(jpool.getResource) match {
case Failure(m) => Left(m)
case Success(m) => Right(m)
}
// return an object to pool
// i believe this may also throw an error if redis is down?
def returnToPool(cache:Jedis): Unit =
Try(jpool.returnResource(cache))
// execute a command -- "del" in this case, (wrapped by
// the two methods above)
def del(key: String) : Option[Long] = {
getFromPool match {
case Left(m) => None
case Right(m) => Try(m.del(key)) eventually returnToPool(m) match {
case Success(r) => Option(r)
case Failure(r) => None
}
}
}
}
Not an exact answer, but I moved on after doing some performance testing. Using the standard java-ish exception blocks ended up being much faster at high iterations (at 10,000 iterations, it was about 2.5x faster than the (bad) code above). That also cleaned up my code, although it's more verbose.
So the answer I arrived at is to use the Java-style exception blocks which provide for the finally construct. I believe it should be significantly faster, as long as exceptions are a very rare occurance.
In my scala code, I have some nested Try() match {}, which look ugly:
import scala.util._
Try(convertJsonToObject[User]) match {
case Success(userJsonObj) =>
Try(saveToDb(userJsonObj.id)) match {
case Success(user) => Created("User saved")
case _ => InternalServerError("database error")
}
case _ => BadRequest("bad input")
}
Is there any better way of writing such code?
There's a bunch of ways to solve this problem. I'll give you one possibility. Consider this cleaned up version of your code:
trait Result
case class BadRequest(message:String) extends Result
case class InternalServerError(message:String) extends Result
case class Created(message:String) extends Result
def processRequest(json:String):Result = {
val result =
for{
user <- Try(parseJson(json))
savedUser <- Try(saveToDb(user))
} yield Created("saved")
result.recover{
case jp:JsonParsingException => BadRequest(jp.getMessage)
case other => InternalServerError(other.getMessage)
}.get
}
def parseJson(json:String):User = ...
def saveToDb(user:User):User = ...
The caveat to this code is that it assumes that you can differentiate the json parsing failure from the db failure by the exception each might yield. Not a bad assumption to make though. This code is very similar to a java try/catch block that catches different exception types and returns different results based on catching those different types.
One other nice thing about this approach is that you could just define a standard recovery Partial Function for all kinds of possible exceptions and use it throughout your controllers (which I'm assuming this code is) to eliminate duplicate code. Something like this:
object ExceptionHandling{
val StandardRecovery:PartialFunction[Throwable,Result] = {
case jp:JsonParsingException => BadRequest(jp.getMessage)
case sql:SQLException => InternalServerError(sql.getMessage)
case other => InternalServerError(other.getMessage)
}
}
And then in your controller:
import ExceptionHandling._
result.recover(StandardRecovery).get
Another approach is to define implicit reads for User (if using Play Framework) and then doing something like
someData.validate[User].map { user =>
saveToDb(user.id) match { // you can return Try from saveToDb
case Success(savedUser) => Created("User saved")
case Failure(exception) => InternalServerError("Database Error")
}
}.recoverTotal {
e => BadRequest(JsError.toFlatJson(e))
}
Try(convertJsonToObject[User]).map([your code]).toOption.getOrElse(fallback)