John Threepwood
John Threepwood

Reputation: 16143

How to tell the Scala compiler that a while loop will return a value?

Some algorithms execute a while loop with condition true and will (for sure) end at some point with a return statement inside the body of the while loop. E.g.:

def foo: Int = {
  while(true) {
    // At some time, the while loop will do a return statement inside its body
    if( ... )
      return 0
  }
}

Simple example (without semantic sense):

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
}

The Scala compiler complains about a type mismatch, because the while loop has type Unit and the compiler does not know, that the while loop will at some point return a value. We could fix this with a workaround like:

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  0 // !
}

But this looks ugly. Is there a better workaround ? Or even a better solution for this kind of problem ?

Upvotes: 2

Views: 2776

Answers (4)

Dave Griffith
Dave Griffith

Reputation: 20515

For just these occasions, I have a "forever" construct defined in my personal standard library.

forever{
}

is in all ways equivalent to

while(true){
}

except that forever has type Nothing while the equivalent while construct has type Unit. Just one of those small extension capabilities that makes Scala such a joy.

Upvotes: 2

Jan
Jan

Reputation: 1777

A more idiomatic way would be to use recursion. Something like this:

  def foo: Int = { 
    import scala.annotation.tailrec
    @tailrec def whileUnderTen(i: Int):Int = if ( i < 10) whileUnderTen(i+1) else 0
    whileUnderTen(0)
  }

Upvotes: 3

DaoWen
DaoWen

Reputation: 33019

Maybe you should just use tail recursion instead. It should end up compiling down to very similar bytecode:

import scala.annotation.tailrec

def foo: Int = {
  @tailrec def bar(i: Int): Int = {
    val j = i + 1
    if (j == 10) return 0
    else bar(j)
  }
  bar(0)
}

You might even want to make use of the default parameter value support:

@tailrec def foo(i: Int = 0): Int = {
  val j = i + 1
  if (j == 10) return 0
  else foo(j)
}

Note that this way requires you to call the function as foo() not foo since it has an argument.

Upvotes: 8

jqno
jqno

Reputation: 15520

You could throw an exception:

def foo: Int = {
  var i = 0
  while(true) {
    i += 1
    if(i == 10)
      return 0
  }
  throw new IllegalStateException("This should never happen")
}

The compiler will stop complaining about the type mismatch, and since the while loop always returns something, the exception will never be thrown. And if it is, you will quickly find out where you did something wrong :).

There are other ways to write this loop which are more idomatic and Scala-esque, but given the code you provided, this will get the job done in a clear and simple way.

Upvotes: 14

Related Questions