The following is a pretty common play framework 2 controller:
def save(ideaId : Long) = CORSAction { request =>
Idea.findById(ideaId).map { idea =>
request.body.asJson.map { json =>
json.asOpt[Comment].map { comment =>
comment.copy(idea = idea).save.fold(
errors => JsonBadRequest(errors),
comment => Ok(toJson(comment).toString)
)
}.getOrElse (JsonBadRequest("Invalid Comment entity"))
}.getOrElse (JsonBadRequest("Expecting JSON data"))
}.getOrElse (JsonBadRequest("Could not find idea with id '%s'".format(ideaId)))
}
I find it a bit annoying all those nested .maps, and I also find a bit tedious that each error handling is at the bottom
How would you improve it to make more readable, and, at the same time remain as functional idiomatic scala code?
I was thinking perhaps something like this (it’s seudo code, still does not compile)
def save(ideaId : Long) = CORSAction { request =>
val idea = Idea.findById(ideaId).getOrElse(
return JsonBadRequest("Could not find idea with id '%s'".format(ideaId)))
val json = request.body.asJson.getOrElse(
return JsonBadRequest("Expecting JSON data"))
val comment = json.asOpt[Comment].getOrElse(
return JsonBadRequest("Invalid Comment entity"))
comment.copy(idea = idea).save.fold(
errors => JsonBadRequest(errors),
comment => Ok(toJson(comment).toString)
)
}
ps: I know It would be much better to avoid the return statement…
First to simplify. Suppose I have three methods that take a
Stringand return anOption[String]:I want a method that pipes a string through these and returns the appropriate error message if I get a
Nonealong the way. I could write this:But right, this isn’t pretty. We can use a
for-comprehension and thetoRightmethod onOptionto write a much clearer version:Calling
toRight(msg)on anOptiongives us aLeft(msg)if it’s empty, and aRight(whatever)otherwise. We then have to take the right projection of theEitherwith.right, since Scala’sEitherisn’t right-biased.The equivalent in your case would be something like this:
Not quite as concise as your desired syntax, but the error messages appear in a more logical place, and we’ve gotten rid of the ugly nesting.