4

I made recursive function, just like

require : L (List[Int])

L pattern matching

  1. Nil => Thread.dumpStack()
  2. x :: xs => print(x) + function(xs)
def function(L : List[Int]) {
    L match {
        case Nil => Thread.dumpStack()
        case x :: xs => print(x + " "); function(xs)
    }
}

val l = (1 to 5).toList // function(l)

So I think this function in the stack frame n times, but it occurs one time, I think this function has already found Nil and print out exception Thread.dumpStack.

Is scala compiler smart or other else?

Silvester
  • 2,819
  • 5
  • 22
  • 23
  • -1 Please be clearer: provide a short code sample and a REPL transcript to illustrate the question. – retronym Nov 12 '11 at 14:05

2 Answers2

7

You are observing tail recursion: there is nothing to store from one iteration to the next, so the recursion is essentially turned into a while loop by the compiler. (So, yes, the compiler is smart in that way.)

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
1

As Rex Kerr pointed out, this is the Scala compiler applying a tail call optimization. If you want to know what is compiled in the end, you can run the compiler with an extra argument:

scalac -Xprint:tailcalls yourfile.scala

This will print the intermediate representation after the tailcalls compiler phase. (If you want to learn about all phases, you can also run scalac -Xshow-phases.) For example, on the following input:

object TailRec {
  def foo(l : List[Int]) : Unit = l match {
    case Nil => Thread.dumpStack()
    case x :: xs => println(x); foo(xs)
  }
}

The compiler will print (for the function foo):

def foo(l: List[Int]): Unit = {
  <synthetic> val _$this: TailRec.type = TailRec.this;
  _foo(_$this,l){
    l match {
      case immutable.this.Nil => java.this.lang.Thread.dumpStack()
      case (hd: Int, tl: List[Int])collection.immutable.::[Int]((x @ _), (xs @ _)) => {
        scala.this.Predef.println(x);
        _foo(TailRec.this, xs)
      }
    }
  }
}

The part _foo(_$this,l) looks like a function definition, but it's really a label, and the "call" _foo(TailRec.this, xs) is in fact a jump to that label. So in short, the compiler rewrote a recursive call into what really is a while loop.

The compiler will apply the optimization automatically when it can. If you want to make sure that a function is properly rewritten, you can annotate it with @tailrec, and the compiler will produce an error if it cannot optimize it.

Philippe
  • 9,582
  • 4
  • 39
  • 59