Search code examples
scalarecursiontail-recursiontail-call-optimization

My scala code does not get TCO'ed though it passes @tailrec


I am looking into scala TCO and have written the following code

import scala.annotation.tailrec
final def tailReccursionEx(str:String):List[String]={

  @tailrec 
  def doTailRecursionEx(str:String,pos:Int,accu:List[String]):List[String]={
    if(pos==str.length) return accu
    else{
      doTailRecursionEx(str,pos+1,accu++accu.foldLeft(List[String](str(`pos`).toString)){
                                            (l,ch)=>l:+ch+str(`pos`)})
  }
}

  doTailRecursionEx(str,0,List[String]())
}

I have passed the @tailrec test and I believe that my function is self-recursive tail call. Yet, when I look into the java byte code with

javap -c -private RecursionEx\$\$anonfun\$doTailRecursionEx\$1\$1

I don't see the promised goto for the TCO for self-recursive function. Here is the bytecode.

public RecursionEx$$anonfun$doTailRecursionEx$1$1(java.lang.String, int);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #35; //Field str$2:Ljava/lang/String;
   5:   aload_0
   6:   iload_2
   7:   putfield    #41; //Field pos$1:I
   10:  aload_0
   11:  invokespecial   #93; //Method scala/runtime/AbstractFunction2."<init>":()V
   14:  return

}

Solution

  • I think you need to run javap on a different generated class file. The file you are examining at present corresponds to the closure you use as part of foldLeft. If you try looking at the "RecursionEx$.class" file you should see the tail call recursion. When I compile the code:

    import scala.annotation.tailrec
    
    object RecursionEx {
        @tailrec
        final def doTailRecursionEx(str: String, pos: Int, accu: List[String]): List[String] = {
            if (pos == str.length) return accu
            doTailRecursionEx(str, pos + 1 , accu ++ accu.foldLeft(List[String](str(`pos`).toString)) {
                                (l, ch) => l :+ ch + str(`pos`)
                            })
        }
        def main(args: Array[String]) {
            doTailRecursionEx("mew",0,List[String]())
        }
    }
    

    and then run javap -c -private RecursionEx$ I see the following for the relevant section of code:

    public final scala.collection.immutable.List doTailRecursionEx(java.lang.String, int, scala.collection.immutable.List);
    
      Code:
       0:   iload_2
       1:   aload_1
       2:   invokevirtual   #21; //Method java/lang/String.length:()I
       5:   if_icmpne   10
       8:   aload_3
       9:   areturn
       10:  iload_2
       11:  iconst_1
       12:  iadd
       13:  aload_3
       14:  aload_3
       15:  getstatic   #26; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
       18:  getstatic   #31; //Field scala/Predef$.MODULE$:Lscala/Predef$;
       21:  iconst_1
       22:  anewarray   #17; //class java/lang/String
       25:  dup
       26:  iconst_0
       27:  getstatic   #31; //Field scala/Predef$.MODULE$:Lscala/Predef$;
       30:  aload_1
       31:  invokevirtual   #35; //Method scala/Predef$.augmentString:(Ljava/lang/String;)Lscala/collection/immutable/StringOps;
       34:  iload_2
       35:  invokeinterface #41,  2; //InterfaceMethod scala/collection/immutable/StringLike.apply:(I)C
       40:  invokestatic    #47; //Method scala/runtime/BoxesRunTime.boxToCharacter:(C)Ljava/lang/Character;
       43:  invokevirtual   #53; //Method java/lang/Object.toString:()Ljava/lang/String;
       46:  aastore
       47:  checkcast   #55; //class "[Ljava/lang/Object;"
       50:  invokevirtual   #59; //Method scala/Predef$.wrapRefArray:([Ljava/lang/Object;)Lscala/collection/mutable/WrappedArray;
       53:  invokevirtual   #62; //Method scala/collection/immutable/List$.apply:(Lscala/collection/Seq;)Lscala/collection/immutable/List;
       56:  new #64; //class RecursionEx$$anonfun$doTailRecursionEx$1
       59:  dup
       60:  aload_1
       61:  iload_2
       62:  invokespecial   #67; //Method RecursionEx$$anonfun$doTailRecursionEx$1."<init>":(Ljava/lang/String;I)V
       65:  invokeinterface #73,  3; //InterfaceMethod scala/collection/LinearSeqOptimized.foldLeft:(Ljava/lang/Object;Lscala/Function2;)Ljava/lang/Object;
       70:  checkcast   #75; //class scala/collection/TraversableOnce
       73:  getstatic   #26; //Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
       76:  invokevirtual   #79; //Method scala/collection/immutable/List$.canBuildFrom:()Lscala/collection/generic/CanBuildFrom;
       79:  invokevirtual   #85; //Method scala/collection/immutable/List.$plus$plus:(Lscala/collection/TraversableOnce;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;
       82:  checkcast   #81; //class scala/collection/immutable/List
       85:  astore_3
       86:  istore_2
       87:  goto    0
    

    with a goto at the end, just as you would expect.