Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward reference extends over definition of value shapes

Similar problems 1. and 2. mention real problems of forward referencing in scala. But I found this particular simple case where there is no forward referencing. All blocks are perfecly stand-alone.

def testFunction() = {
  def recursiveMethod(i: Int, j: Int = 3): Unit = i match {
    case 0 => println(s"finished")
    case i => recursiveMethod(i-1)
  }
  val shapes = List[String]()
  def recursive(i: Int): Unit = i
}

Solutions to overcome that problem found so far:

  • Make shapes a lazy val (as in 2.)
  • Rename function recursive to something which is not a prefix of recursiveMethod
  • Remove the optional parameter j: Int = 3
  • Do not have recursiveMethod call itself

Can someone explain to me why these are solutions, although they seem to be completely unrelated to the problem?

like image 336
Mikaël Mayer Avatar asked Sep 11 '13 07:09

Mikaël Mayer


1 Answers

With -Xprint:typer you can see the synthetic that is forward-referenced:

package oops {
  object Test extends scala.AnyRef {
    def <init>(): oops.Test.type = {
      Test.super.<init>();
      ()
    };
    def testFunction(): Unit = {
      def recursiveMethod(i: Int, j: Int = 3): Unit = i match {
        case 0 => scala.this.Predef.println(scala.StringContext.apply("finished").s())
        case (i @ _) => recursiveMethod(i.-(1), recursiveMethod$default$2)
      };
      val shapes: List[String] = immutable.this.Nil;
      def recursive(i: Int): Unit = {
        i;
        ()
      };
      <synthetic> def recursiveMethod$default$2: Int @scala.annotation.unchecked.uncheckedVariance = 3;
      ()
    };
    def main(args: Array[String]): Unit = ()
  }
}

There are at least a couple of tickets related to generating methods close to their origin to avoid these sorts of invisible issues.

Update: for the prurient:

Oldie but goodie

Closer to my heart if not directly related to this problem

I especially liked "rename the function so it's not a prefix of the other function".

This shows how renaming reorders the members:

  abstract trait Oops extends scala.AnyRef {
    def /*Oops*/$init$(): Unit = {
      ()
    };
    def testFunction(): Unit = {
      def recursiveMethod(i: Int, j: Int = 3): Unit = i match {
        case 0 => scala.this.Predef.println(scala.StringContext.apply("finished").s())
        case (i @ _) => recursiveMethod(i.-(1), recursiveMethod$default$2)
      };
      <synthetic> def recursiveMethod$default$2: Int @scala.annotation.unchecked.uncheckedVariance = 3;
      val shapes: List[String] = immutable.this.Nil;
      def xrecursive(i: Int): Unit = {
        i;
        ()
      };
      ()
    }
  };

Needless to add, this must be a bug or a regression. Right?

Update:

Indeed, the sorting test is syntName.toString.startsWith, which explains why renaming the other function makes a difference. This shows how fragile name mangling leaks bugs everywhere. It's like having termites which, every so often, pop out of the woodwork, reminding you that over the last five years they have compromised the structural integrity of the frame.

This is the code with the comment [Martin] This is pretty ugly. So it's not unknown, just in need of someone with free time.

like image 145
som-snytt Avatar answered Oct 13 '22 20:10

som-snytt