Simplify first. Suppose I have three methods that take a String and return Option[String] :
def foo(s: String): Option[String] = if (s.size >= 4) Some(s + "1") else None def bar(s: String): Option[String] = if (s(0) != 'A') Some(s + "2") else None def baz(s: String): Option[String] = if (s toSet ' ') Some(s + "3") else None
I need a method that passes a string through them and returns the corresponding error message if I get None along the way. I could write this:
def all(s: String): Either[String, String] = foo(s).map { x => bar(x).map { y => baz(y).map { z => Right(z) } getOrElse Left("Doesn't contain a space!") } getOrElse Left("Starts with an A!") } getOrElse Left("Too short!")
But right, it's ugly. We can use the for -comprehension and toRight on Option to write a clearer version:
def all(s: String): Either[String, String] = for { x <- (foo(s) toRight "Too short!" ).right y <- (bar(x) toRight "Starts with an A!" ).right z <- (baz(y) toRight "Doesn't contain a space!").right } yield z
Calling toRight(msg) on Option gives us Left(msg) if it is empty, and a Right(whatever) otherwise. Then we must take the right projection of Either with .right , since Scala Either not right.
The equivalent in your case would be something like this:
def save(ideaId: Long) = CORSAction { request => val saveResult = for { idea <- (Idea.findById(ideaId) toRight "Could not find id" ).right json <- (request.body.asJson toRight "Invalid Comment entity").right comment <- (json.asOpt[Comment] toRight "Expecting JSON data" ).right result <- comment.copy(idea = idea).save().right } yield result saveResult.fold( error => JsonBadRequest(error), comment => Ok(toJson(comment).toString) ) }
Not as concise as your desired syntax, but error messages appear in a more logical place, and we got rid of the ugly attachment.
source share