102

I am a newbie scala programmer and came across a weird behavior.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true;
      else false;

    if (elem.head == '(')
      balanceMain(elem.tail, open, count + 1);....

Above basically I want to return true if elem.isEmpty and count == 0. Otherwise, I want to return false.

Now above I have read that there is no need to add a return statement in scala. So I have omitted return above. But it doesn't return the boolean. If I add a return statement as return true. it works perfectly. Why is it so?

Also, why is it considered a bad practice to have return statements in scala

dade
  • 3,340
  • 4
  • 32
  • 53
Jatin
  • 31,116
  • 15
  • 98
  • 163
  • 5
    There is **usually** no need for the return keyword, as long as you break your code into small enough methods. – mauhiz Sep 24 '12 at 07:27
  • 2
    @mauhiz Thanks. Can you please explain it? How will you do it. – Jatin Sep 24 '12 at 07:28
  • 1
    @mauhiz I am also new to Scala, but not programming at all. I think that where its functional programming approach originates. You only define functions to do a specific task. – blakroku May 10 '21 at 10:17
  • `if (count == 0) true; else false;` -- is not it is just `count != 0`? – Mikhail Ionkin Apr 29 '22 at 10:00

6 Answers6

159

It's not as simple as just omitting the return keyword. In Scala, if there is no return then the last expression is taken to be the return value. So, if the last expression is what you want to return, then you can omit the return keyword. But if what you want to return is not the last expression, then Scala will not know that you wanted to return it.

An example:

def f() = {
  if (something)
    "A"
  else
    "B"
}

Here the last expression of the function f is an if/else expression that evaluates to a String. Since there is no explicit return marked, Scala will infer that you wanted to return the result of this if/else expression: a String.

Now, if we add something after the if/else expression:

def f() = {
  if (something)
    "A"
  else
    "B"

  if (somethingElse)
    1
  else
    2
}

Now the last expression is an if/else expression that evaluates to an Int. So the return type of f will be Int. If we really wanted it to return the String, then we're in trouble because Scala has no idea that that's what we intended. Thus, we have to fix it by either storing the String to a variable and returning it after the second if/else expression, or by changing the order so that the String part happens last.

Finally, we can avoid the return keyword even with a nested if-else expression like yours:

def f() = {
  if(somethingFirst) {
    if (something)      // Last expression of `if` returns a String
     "A"
    else
     "B"
  }
  else {
    if (somethingElse)
      1
    else
      2

    "C"                // Last expression of `else` returns a String
  }

}

dhg
  • 52,383
  • 8
  • 123
  • 144
  • 6
    By all the Gods, thanks! I was fighting with the same problem for hours (also making the course in Coursera) and wasn't able to understand why the return was required. – Igor Rodriguez May 03 '14 at 19:39
  • 2
    for the first example, what happens if you add returns. i.e `return "A"` and `return "B"` ? – SamAko Aug 31 '15 at 00:02
  • 1
    @T.Rex it would return to the caller of f() with the value "A" or "B". But as I explained in my answer. Never use return in Scala. If statements in Scala work in a functional way. They evaluate to something with a type. Like the ternary operator in Java (?:). Example: val foo = if (mybool) "A" else "B" - foo will be a String containing either "A" or "B". Likewise think of a function not returning something but evaluating to the value of the last expression in it. – Grmpfhmbl Sep 07 '16 at 20:16
27

This topic is actually a little more complicated as described in the answers so far. This blogpost by Rob Norris explains it in more detail and gives examples on when using return will actually break your code (or at least have non-obvious effects).

At this point let me just quote the essence of the post. The most important statement is right in the beginning. Print this as a poster and put it to your wall :-)

The return keyword is not “optional” or “inferred”; it changes the meaning of your program, and you should never use it.

It gives one example, where it actually breaks something, when you inline a function

// Inline add and addR
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m) // inlined add

scala> sum(33, 42, 99)
res2: Int = 174 // alright

def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m) // inlined addR

scala> sumR(33, 42, 99)
res3: Int = 33 // um.

because

A return expression, when evaluated, abandons the current computation and returns to the caller of the method in which return appears.

This is only one of the examples given in the linked post and it's the easiest to understand. There're more and I highly encourage you, to go there, read and understand.

When you come from imperative languages like Java, this might seem odd at first, but once you get used to this style it will make sense. Let me close with another quote:

If you find yourself in a situation where you think you want to return early, you need to re-think the way you have defined your computation.

Grmpfhmbl
  • 1,119
  • 12
  • 24
  • 17
    I don't agree with Rob, IMO you must not use `return` only in lambda expressions, but this is a shitty design choice in the language, the code should not even compile (in python this is avoided by having `lambda` keyword without the return statement)... in each other cases I don't see a real problem in using `return` if you want to return a value (and yes exit from method execution because is what return is used for in all languages!) – daveoncode Nov 23 '17 at 11:19
  • 4
    You're free to have your opinion, but many people agree that using return in Scala is at least bad style. I dare you to find official Scala doc examples that use return. It is AFAIK not even explained in the official docs, only in the spec document (chap 6.20). To quote Martin Odersky himself (Programming in Scala) "The recommended style for methods is in fact to avoid having explicit, and especially multiple, return statements. Instead, think of each method as an expression that yields one value, which is returned." First ed of this book is available free online http://www.artima.com/pins1ed/ – Grmpfhmbl Jan 02 '18 at 10:45
5

I don't program Scala, but I use another language with implicit returns (Ruby). You have code after your if (elem.isEmpty) block -- the last line of code is what's returned, which is why you're not getting what you're expecting.

EDIT: Here's a simpler way to write your function too. Just use the boolean value of isEmpty and count to return true or false automatically:

def balanceMain(elem: List[Char]): Boolean =
{
    elem.isEmpty && count == 0
}
jmdeldin
  • 5,354
  • 1
  • 28
  • 21
  • Thanks. But i only want to return if elem.isEmpty && count ==0 return true else continue the block. The above will return even if it is false. – Jatin Sep 24 '12 at 07:26
  • @Jatin: Ah, didn't realize that. Early and `explicit` return would be appropriate in this case then. – jmdeldin Sep 24 '12 at 14:39
  • @Frank: Nor is it defined in the OP's code. I assumed it was a method call. – jmdeldin Sep 24 '12 at 14:39
5

By default the last expression of a function will be returned. In your example there is another expression after the point, where you want your return value. If you want to return anything prior to your last expression, you still have to use return.

You could modify your example like this, to return a Boolean from the first part

def balanceMain(elem: List[Char]): Boolean = {
  if (elem.isEmpty) {
    // == is a Boolean resulting function as well, so your can write it this way
    count == 0
  } else {
    // keep the rest in this block, the last value will be returned as well
    if (elem.head == "(") {
      balanceMain(elem.tail, open, count + 1)
    }
    // some more statements
    ...
    // just don't forget your Boolean in the end
    someBoolExpression
  }
}
Tharabas
  • 3,402
  • 1
  • 30
  • 29
4

Don't write if statements without a corresponding else. Once you add the else to your fragment you'll see that your true and false are in fact the last expressions of the function.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true
      else
        false
    else
      if (elem.head == '(')
        balanceMain(elem.tail, open, count + 1)
      else....
Bart Schuller
  • 2,725
  • 19
  • 18
1

Use case match for early return purpose. It will force you to declare all return branches explicitly, preventing the careless mistake of forgetting to write return somewhere.

Zhu Jinxuan
  • 406
  • 2
  • 8