I have following action
def login: Result = Action(parse.json) { request =>
if (/* Let's we say here is some validation */) {
return BadRequest("bad")
}
Ok("all ok")
}
This is giving me an Error. Intellij is suggesting Action[JsValue]
type. When I say return will be of that type, I am getting again error on BadRequest
line, because types doesn't match.
I tried searching about this problem, and I found some answers which are suggesting to set Action[AnyContent]
as return type. But I am STILL getting error.
Also I need to return from if
...I don't want to write else
after that if
, because in some more complex function most probably I will have few if
statements which should break action, and if I use if/else
approach, code will be nightmare.
Of course it does. In Scala, a "return" statement inside a nested anonymous function is implemented by throwing and catching a NonLocalReturnException. It says so in the Scala Language Specification, section 6.20.
This is done because of for comprehensions, for people to be able to write code like this:
def loop() = {
for (i <- 0 until 20) {
if (someCondition) return
}
}
That loop is actually equivalent to:
(0 until 20).foreach { i => if (someCondition) return }
Can you see the anonymous function? Can you see that the "return" in question doesn't refer to returning from that anonymous function alone? In my opinion this was a design mistake. On the other hand, in a language like Scala, "return" isn't needed anyway.
So you have an anonymous function in there:
Action(parse.json) { request => ... this one here ... }
And inside that function you're using "return" which under the hood triggers an exception.
So the general rule of thumb in Scala - NEVER, EVER USE RETURN. It's an expression-oriented language anyway. You don't need it.
Action(parse.json) { request =>
if (/* Let's we say here is some validation */)
BadRequest("bad")
else
Ok("all ok")
}
There, much more idiomatic. BTW - you also don't have "break" or "continue". Get used to working without them. Also, about your opinion:
Also I NEED to return from if...I don't want to write else after that if, because in some more complex function most probably I will have few if statements which should break action, and if I use if/else approach, code will be nightmare.
That is wrong and I REALLY HATE working with code that relies on return, break or continue for short-circuiting the logic PRECISELY because complex logic gets messy and I want to have a clear view of:
- invariants, if we are talking about a loop
- exit conditions
- the branches / paths
Using return, break or continue destroys clarity on all 3 points. They aren't much better than GOTO jumps. And if you have complex functions, break them into multiple functions if reading it starts to become a problem.
Also, much better than multiple "if/else" branches are "match" statements. Once you get used to them, you'll love them, especially given that the compiler can even protect you in some cases in which you're missing a branch.
You cannot use return
in this context because the Scala compiler misinterprets what you mean by return
. You intended for the return
to return from the function that is being passed to Action.apply
, i.e. this block:
{ request =>
if (/* Let's we say here is some validation */) {
return BadRequest("bad")
}
Ok("all ok")
}
However, in Scala return
cannot be used to return
from functions, only methods. Any return
is interpreted as returning from the nearest enclosing method. In this case, the compiler thought you were returning from login
.
You have the wrong type on login
. login
should be a Action[JsValue]
. With that correct type, the compiler will complain about the return because you are returning a SimpleResult
instead of the expected Action[JsValue]
. With the incorrect type Result
, the compiler accepts the return
but then isn't happy because the return value of Action.apply
is always a type of Action
.
The reason return
works this way is to support loops like:
def allPositive(l: List[Int]): Boolean = {
l.foreach { x => if (x < 0) return false }
true
}
But using return
is unidiomatic. The return
in the above loop is implemented with exception throwing so it is inefficient. And return
can make for very confusing code:
def login: Result = Action(parse.json) { request =>
/*
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL with return OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
*/
Ok("all ok")
}
Obviously this login
always returns Ok
. Oh wait...there is a return
buried in there.