Search code examples
scalatail-call-optimization

What is the Scala annotation to ensure a tail recursive function is optimized?


I think there is @tailrec annotation to ensure the compiler will optimize a tail recursive function. Do you just put it in front of the declaration? Does it also work if Scala is used in scripting mode (for instance using :load <file> under REPL)?


Solution

  • From the "Tail calls, @tailrec and trampolines" blog post:

    • In Scala 2.8, you will also be able to use the new @tailrec annotation to get information about which methods are optimised.
      This annotation lets you mark specific methods that you hope the compiler will optimise.
      You will then get a warning if they are not optimised by the compiler.
    • In Scala 2.7 or earlier, you will need to rely on manual testing, or inspection of the bytecode, to work out whether a method has been optimised.

    Example:

    you could add a @tailrec annotation so that you can be sure that your changes have worked.

    import scala.annotation.tailrec
    
    class Factorial2 {
      def factorial(n: Int): Int = {
        @tailrec def factorialAcc(acc: Int, n: Int): Int = {
          if (n <= 1) acc
          else factorialAcc(n * acc, n - 1)
        }
        factorialAcc(1, n)
      }
    }
    

    And it works from the REPL (example from the Scala REPL tips and tricks):

    C:\Prog\Scala\tests>scala
    Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
    Type in expressions to have them evaluated.
    Type :help for more information.
    
    scala> import scala.annotation.tailrec
    import scala.annotation.tailrec
    
    scala> class Tails {
         | @tailrec def boom(x: Int): Int = {
         | if (x == 0) throw new Exception("boom!")
         | else boom(x-1)+ 1
         | }
         | @tailrec def bang(x: Int): Int = {
         | if (x == 0) throw new Exception("bang!")
         | else bang(x-1)
         | }
         | }
    <console>:9: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
           @tailrec def boom(x: Int): Int = {
                        ^
    <console>:13: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden
           @tailrec def bang(x: Int): Int = {
                        ^