2

I have the following code, but I can't get it to work. As soon as I place a while loop inside the case, it's returning a unit, no matter what I change within the brackets.

case While(c, body) =>
    while (true) {
      eval(Num(1))
    }
}

How can I make this while loop return a non-Unit type?

I tried adding brackets around my while condition, but still it doesn't do what it's supposed to.

Any pointers?

Update

A little more background information since I didn't really explain what the code should do, which seems to be handy if I want to receive some help;

I have defined a eval(exp : Exp). This will evaluate a function. Exp is an abstract class. Extended by several classes like Plus, Minus (few more basic operations) and a IfThenElse(cond : Exp, then : Exp, else : Exp). Last but not least, there's the While(cond: Exp, body: Exp).

Example of how it should be used;

eval(Plus(Num(1),Num(4)) would result in NumValue(5). (Evaluation of Num(v : Value) results in NumValue(v). NumValue extends Value, which is another abstract class).

eval(While(Lt(Num(1),Var("n")), Plus(Num(1), Var("n"))))

Lt(a : Exp, b : Exp) returns NumValue(1) if a < b.

Community
  • 1
  • 1
Sander
  • 116
  • 1
  • 10
  • 4
    While and do-while don't have return values in scala. – vertti Mar 20 '12 at 11:38
  • 3
    I'm not sure where you're going with this loop. Not only will a while-loop never return a value, but your while loop will never return at all (unless an exception occurs). What do you want to return? – thoredge Mar 20 '12 at 15:11
  • @thoredge as stated below somewhere, the 'true' condition was for testing purposes. ;) I hope my explaination above will give you a beter idea of what I'm trying to do. Thanks. – Sander Mar 20 '12 at 20:24
  • 1
    I'm not sure what your edit adds to the question. You haven't explained anything about what you want the loop to return. – Ken Bloom Mar 20 '12 at 23:45

4 Answers4

10

It's probably clear from the other answer that Scala while loops always return Unit. What's nice about Scala is that if it doesn't do what you want, you can always extend it.

Here is the definition of a while-like construct that returns the result of the last iteration (it will throw an exception if the loop is never entered):

def whiley[T](cond : =>Boolean)(body : =>T) : T = {
  @scala.annotation.tailrec
  def loop(previous : T) : T = if(cond) loop(body) else previous
  if(cond) loop(body) else throw new Exception("Loop must be entered at least once.")
}

...and you can then use it as a while. (In fact, the @tailrec annotation will make it compile into the exact same thing as a while loop.)

var x = 10
val atExit = whiley(x > 0) {
  val squared = x * x
  println(x)
  x -= 1
  squared
}
println("The last time x was printed, its square was : " + atExit)

(Note that I'm not claiming the construct is useful.)

Philippe
  • 9,582
  • 4
  • 39
  • 59
  • Interesting. It took me a few tries to understand how the body was getting executed. Personally, I'd write this while loop using a `var` and a `while` loop internally so that it would be clearer to read. – Ken Bloom Mar 20 '12 at 23:49
  • This answer actually got me to my mistake. I was confused by the error I got. Which was telling me the compiler found the while loop, but that it was expecting a Value. The while loop ofcourse returned a Unit. But the error indicated the error within the while loop, while the bug was on the outside. (simply had to use some variables to get the result I wanted). Thanks! – Sander Mar 21 '12 at 19:21
8

Which iteration would you expect this loop to return? If you want a Seq of the results of all iterations, use a for expression (also called for comprehension). If you want just the last one, create a var outside the loop, set its value on each iteration, and return that var after the loop. (Also look into other looping constructs that are implemented as functions on different types of collections, like foldLeft and foldRight, which have their own interesting behaviors as far as return value goes.) The Scala while loop returns Unit because there's no sensible one size fits all answer to this question.

(By the way, there's no way for the compiler to know this, but the loop you wrote will never return. If the compiler could theoretically be smart enough to figure out that while(true) never terminates, then the expected return type would be Nothing.)

Heiko Seeberger
  • 3,702
  • 21
  • 20
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • The while(true) was simply a test to see if there was an error in my condition evaluation. But since the error was still there.. there's another bug somewhere, which I'm trying to find/understand. See also the update in the initial post. Thanks. – Sander Mar 20 '12 at 20:07
7

The only purpose of a while loop is to execute a side-effect. Or put another way, it will always evaluate to Unit.

If you want something meaningful back, why don't you consider using an if-else-expression or a for-expression?

Heiko Seeberger
  • 3,702
  • 21
  • 20
2

As everyone else and their mothers said, while loops do not return values in Scala. What no one seems to have mentioned is that there's a reason for that: performance.

Returning a value has an impact on performance, so the compiler would have to be smart about when you do need that return value, and when you don't. There are cases where that can be trivially done, but there are complex cases as well. The compiler would have to be smarter, which means it would be slower and more complex. The cost was deemed not worth the benefit.

Now, there are two looping constructs in Scala (all the others are based on these two): while loops and recursion. Scala can optimize tail recursion, and the result is often faster than while loops. Or, otherwise, you can use while loops and get the result back through side effects.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • I know a while loop itself isn't supposed to return anything but Unit. But, I updated my initial post, and I hope this will clearify my story a bit. Thanks anyway! – Sander Mar 20 '12 at 20:06
  • @KenBloom Well, I don't have links to previous discussion to this effect, and I may be conflating while loops with assignments wrt performance issues. However, that Scala only has two looping constructs is easily verified. – Daniel C. Sobral Mar 21 '12 at 00:45
  • 1
    I would say that while loops don't return a value because they semantically don't *have* a value; performance has nothing to do with it. You could say that a while loop returns the sequence of the values of each iteration, but that's just a for loop. You could say it returns the value of its last iteration, but a while loop can execute 0 times, so it would have to be an Option at best. You use while loops to evaluate some code repeatedly, which means you're doing it for the side effects of the code. Returning any value would just be clunky and at odds with the reason programmers use them. – Ben Mar 21 '12 at 06:16