4
votes

I'm reading Programming in Scala by M. Odersky and he says that

Functions like approximate, which call themselves as their last action, are called tail recursive.

So, I tried this:

object Main extends App {
    implicit val mc = new MyClass(8)
    val ti = new TestImplct
    ti.test
}

class TestImplct {
  def test(implicit mc : MyClass): Unit = {
    println(mc.i)
    mc.i -= 1
    if(mc.i < 0){
      throw new IllegalArgumentException
    }
    test
  }
}

class MyClass(var i : Int)

IDEONE DEMO

But it generates the following stack trace

 Exception in thread "main" java.lang.IllegalArgumentException
    at TestImplct.test(Main.scala:13)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)
    at TestImplct.test(Main.scala:15)

Which means that it generates a new stack frame for each recursive call. But the last action is to call to itself. What's wrong and how to make it tail-recursive?

Why doesn't compiler do tail-call optimization?

2
Do you have an implicit instance of MyClass in scope? - Samar
@Samar Yes, I have. Look at the demo - stella
I see you have defined the class. But I dont see where you instantiated it. - Samar
@Samar Ah, you're right. i didn;t attach the full code. My appologies. - stella
Its working fine. Why do you think its not tail recursive? Its just exiting because of the if condition. - Samar

2 Answers

9
votes

You can try marking the method with @tailrec annotation. If you do that, compilation will fail and will show you why compiler couldn't optimize this to be tail recursive:

Main.scala:12: error: could not optimize @tailrec annotated method test: it is neither private nor final so can be overridden

Indeed, if you make the method final, it works as expected.

2
votes

Your code works correctly. Your value in the mc object decrease with every step. At the last step you get the exception.

You can change the return type of your function to be Boolean for example and return false when your value is < 0 otherwise you return true.

I would suggest, you use '@tailrec' annotation in order to get checked the recursive function call by compiler.