Martin GOYOT Martin GOYOT - 4 months ago 15
Scala Question

How to early return in Scala

I am learning Scala at the moment. One thing that I like to do is early returns. I'm convinced that this is far easier for everyone to read as we just remove the invalid states before. Now, as Scala is a functional language and I've read that cutting computation is bad functional style, I'm wondering if there is some trick or functional programming equivalent to early return.

This is code I would write, to be completely clear, this is just a dumb example, I'm not looking for the special hack of my special case, but more for a general advice on how to deal with these.

if (request.headers.get(session_header).isEmpty) {
BadRequest(session_not_set)
} else {
Ok(CartHelper.getCart(session, user))
}


Now, what I'm tempted to do is:

if (request.headers.get(session_header).isEmpty) {
BadRequest(session_not_set)
return;
}

Ok(CartHelper.getCart(session,user))


If you have any hint for me!

Answer

In some instances the return keyword cannot be avoided, but it doesn't look like you have that problem currently.

Scenario 1: The single condition scenario, your current one. In this instance you can avoid using return with a very simple if else.

def doSomething: AnyContent = {
  if (request.headers.get(session_header).isEmpty) {
    BadRequest(session_not_set)
  } else {
    Ok(CartHelper.getCart(session,user))
  }
}

If the session not being set is a common problem, you can simply have a guard around it with a partial function.

def requireSession(req: Request)(
   pf: Session => AnyContent
): AnyContent = {
   request.headers.get(session_header)
     .fold(BadRequest("Session not set"))(pf(_))
}

And then:

// Assuming Play framework being used here
def getCart: AnyContent = Action { implicit req =>
  requireSession(req) { session => Ok(CartHelper.getCart(session, user) }
}

Scenario 2: Break loop using return, or the so called early return is usually a performance improvement element.

One apparently valid use of return in Scala which is something that seems unavoidable is a situation where you are iterating a collection for the first of something. Obviously you can have that abstracted away for you using collection.find and other helper methods pre-build in the standard lib, but for the sake of argument.

def inList[T](l: List[T], value: T): Boolean = {
  for (el <- l) {
    // break the loop for the first match found
    // for the sake of efficiency.
    if (el == value) return true;
  }
  false;
}

Even in situations like this return is avoidable, by using a different construct, and there's always a recursive version of something you can use to replace an apparently impossible return inside dan iteration.