2

Here is an artificial toy example that demonstrates my problem:

def sscce(): Int = {
  val rand = new Random()
  var count = 0
  while (true) {   // type mismatch; found: Unit, required: Int
    count += 1
    if (rand.nextInt() == 42) return count
  }
}

How can I help the compiler understand that this method will always return an Int?

I know the above toy example could easily be refactored to get rid of the infinite loop altogether, but I really want to have the infinite loop in my actual code. Trust me on this ;)

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 2
    Your code doesn't, technically speaking, return an Int. It returns an Int *if* the function returns at all. I suspect this distinction is the problem. What are you trying to solve? You say you really want an infinite loop, but perhaps your real world problem can have another solution. – Daenyth Mar 05 '15 at 17:43
  • 1
    I really love infinite loops, but they take forever to finish. – Michael Zajac Mar 05 '15 at 17:44
  • You can't prove that it will always be executed because it won't always be executed. It will usually be executed, but scala doesn't provide a `Usually[Int]` type. I'd be shocked if there wasn't a better way to do this instead of using the infinite loop. – Daenyth Mar 05 '15 at 18:00
  • possible duplicate of [Scala while(true) type mismatch? Infinite loop in scala?](http://stackoverflow.com/questions/10802917/scala-whiletrue-type-mismatch-infinite-loop-in-scala) – fredoverflow Mar 05 '15 at 18:19

5 Answers5

6

Always return an Int:

def sscce(): Int = {
  val rand = new Random()
  var count = 0
  while (true) {
    count += 1
    if (rand.nextInt() == 42) return count
  }
  count // <-- this
}
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
4

You can also do:

def foo: Int = {
  ...
  while(true) {
    ... return ...
  }
  throw new IllegalStateException  // unreachable
}

this will typecheck because the type of the throw is Nothing, which is a subtype of Int.

Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
2

See this question. While loops don't return a value. i.e. they return Unit which is the last statement in your function. So, the definition says it returns an Int but it actually returns Unit thus the type error. @ionut's answer fixes the type error by returning count as the last statement or here is a recursive approach.

def sscce(): Int = {
  val rand = new Random()
  def ssccer(count: Int): Int = {
    if(rand.nextInt == 42) return count
    else ssccer(count + 1)
  }
  ssccer(0)
}
Brian
  • 20,195
  • 6
  • 34
  • 55
2

Per the SLS, a while loop is executed similarly to:

def whileLoop(cond: => Boolean)(body: => Unit): Unit  =
  if (cond) { body ; whileLoop(cond)(body) } else {}

ie., it returns Unit. So the compiler sees the while as the last statement in sscce(), and therefore assumes that you're trying to return Unit. I don't think it's smart enough to realize that return count will eventually always return an Int.

The simple solution is to follow the suggestion of @Brian or @IonutGStan, and force it to return count, whether it truly needs it or not.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
2

From a code quality standpoint, it would be good to ditch the while(true) loop and replace it with something more readable. As a nice side effect, it also solves your problem:

def sscce(): Int = {
  val rand = new Random()
  var count = 1
  while (rand.nextInt() != 42) {
    count += 1
  }
  count
}
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142