Scala while (true) type mismatch? Endless loop in scala?

Why the following code

def doSomething() = "Something" var availableRetries: Int = 10 def process(): String = { while (true) { availableRetries -= 1 try { return doSomething() } catch { case e: Exception => { if (availableRetries < 0) { throw e } } } } } 

creates the following compiler error

 error: type mismatch; found : Unit required: String while (true) { ^ 

?

This works fine in C #. The while loop is forever, therefore it cannot be completed, therefore it cannot lead to anything other than a line. Or how to make an infinite loop in Scala?

+7
source share
6 answers

Based on senia , elbowich and dave that I used:

 @annotation.tailrec def retry[T](availableRetries: Int)(action: => T): T = { try { return action } catch { case e: Exception if (availableRetries > 0) => { } } retry(availableRetries - 1)(action) } 

which can then be used as elbowich and dave solutions:

 retry(3) { // some code } 
+1
source

Unlike C # (both Java and C and C ++), which are operator-based languages, Scala is an expression-based language. This is basically a big plus in terms of composability and readability, but in this case the difference bit you.

The Scala method implicitly returns the value of the last expression in the method

 scala> def id(x : String) = x id: (x: String)String scala> id("hello") res0: String = hello 

In Scala, almost everything is an expression. Things that look like statements are still expressions that return a value of type Unit. The value can be written as ().

 scala> def foo() = while(false){} foo: ()Unit scala> if (foo() == ()) "yes!" else "no" res2: java.lang.String = yes! 

No compiler for the Turing equivalent language can detect all infinite loops (cf the problem of stopping Turing), so most compilers work very little to detect them. In this case, the type "while (someCondition) {...}" is a unit, regardless of what someCondition is, even if it is true.

 scala> def forever() = while(true){} forever: ()Unit 

Scala determines that the declared return type (String) is incompatible with the actual return type (Unit), which is the type of the last expression (while ...)

 scala> def wtf() : String = while(true){} <console>:5: error: type mismatch; found : Unit required: String def wtf() : String = while(true){} 

Answer: add an exception to the end

 scala> def wtfOk() : String = { | while(true){} | error("seriously, wtf? how did I get here?") | } wtfOk: ()String 
+16
source

The functional way to define an infinite loop is recursion:

 @annotation.tailrec def process(availableRetries: Int): String = { try { return doSomething() } catch { case e: Exception => { if (availableRetries < 0) { throw e } } } return process(availableRetries - 1) } 

The elbowich retry function without an internal loop function:

 import scala.annotation.tailrec import scala.util.control.Exception._ @tailrec def retry[A](times: Int)(body: => A): Either[Throwable, A] = { allCatch.either(body) match { case Left(_) if times > 1 => retry(times - 1)(body) case x => x } } 
+7
source

The compiler is not smart enough to know that you cannot exit the while loop, unfortunately. However, it is easy to fool, even if you cannot intelligently generate a member of the return type - just throw an exception.

 def process(): String = { while (true) { ... } throw new Exception("How did I end up here?") } 

Now the compiler will understand that even if he avoids the while loop, he cannot return the value there, so do not worry about the while loop having the return type of Unit (i.e. does not return the value).

+4
source
 import scala.annotation.tailrec import scala.util.control.Exception._ def retry[A](times: Int)(body: => A) = { @tailrec def loop(i: Int): Either[Throwable, A] = allCatch.either(body) match { case Left(_) if i > 1 => loop(i - 1) case x => x } loop(times) } retry(10) { shamelessExceptionThrower() } 
+4
source

edit: I just noticed a valid return statement. The return statement inside the while loop will be ignored. For example, in REPL:

 scala> def go = while(true){return "hi"} <console>:7: error: method go has return statement; needs result type def go = while(true){return "hi"} ^ 

You told the compiler that the process() method returns a String , but the body of the method is just a while that returns nothing (this is a Unit or Java void ). Either change the return type, or add a line after the while loop.

 def process(): Unit = { while(true){...} } 

or

 def process(): String = { while(true){...} "done" } 
0
source

All Articles