stack overflow on overriding lazy val in scala

3.4k views Asked by At

I have trimmed my code down to the following. I am confused why I am getting a stack overflow between the two filter methods (one in my trait and one in my superclass)

object TestingOutTraits {

  val TestHandler = new Object with MySuper with MyTrait {
    override lazy val createdFilter = {
      "second part"
    }
  }

  def main(args: Array[String]) = {
    val result : String = TestHandler.start()
    System.out.println("result="+result)
  }
}

trait MySuper {

  protected def filter: String = {
    "first part to->"
  }

  def start() = {
    filter
  }
}

trait MyTrait { self: MySuper =>

  lazy val createdFilter = {
    "override this"
  }

  protected override def filter: String = {
    self.filter + createdFilter
  }
}

This is scala 2.9. Any ideas what is going on here?

EDIT: The stack trace makes no sense on how it jumps back and forth too(I should have included it in original post)...

at MyTrait$class.filter(TestingOutTraits.scala:34)
at TestingOutTraits$$anon$1.filter(TestingOutTraits.scala:4)
at MyTrait$class.filter(TestingOutTraits.scala:34)
at TestingOutTraits$$anon$1.filter(TestingOutTraits.scala:4)

thanks, Dean

3

There are 3 answers

6
wingedsubmariner On BEST ANSWER

The call self.filter in MyTrait.filter invokes itself, leading to infinite recursion that blows the stack.

Instead, have MyTrait extend MySuper, and use super.filter:

trait MyTrait extends MySuper {
  lazy val createdFilter = {
    "override this"
  }

  protected override def filter: String = {
    super.filter + createdFilter
  }
}
0
Dean Hiller On

Well, I know why the infinite recursion though this seems like a linearization bug in scala and I am not sure how to work around it yet either :(.

hmmmm, so it turns out the compiler is sticking a filter method in my new Object for some reason. I found this out with the print:mixin on scalac like so

    $ scalac -Xprint:mixin TestingOutTraits.scala
      [[syntax trees at end of mixin]]// Scala source: TestingOutTraits.scala
    package <empty> {
      final object TestingOutTraits extends java.lang.Object with ScalaObject {
        private[this] val TestHandler: MySuper = _;
        <stable> <accessor> def TestHandler(): MySuper = TestingOutTraits.this.TestHandler;
          def main(args: Array[java.lang.String]): Unit = {
          val result: java.lang.String = TestingOutTraits.this.TestHandler().start();
          java.this.lang.System.out.println("result=".+(result))
          };
          def this(): object TestingOutTraits = {
          TestingOutTraits.super.this();
          TestingOutTraits.this.TestHandler = {
            new anonymous class TestingOutTraits$$anon$1()
          };
          ()
          }
          };
          abstract trait MySuper extends java.lang.Object with ScalaObject {
          def filter(): java.lang.String;
          def start(): java.lang.String
          };
          abstract trait MyTrait extends java.lang.Object with ScalaObject { self: MyTrait =>
            def createdFilter(): java.lang.String;
            override def filter(): java.lang.String
          };
          abstract trait MySuper$class extends  {
          def filter($this: MySuper): java.lang.String = "first part to->";
          def start($this: MySuper): java.lang.String = $this.filter();
          def /*MySuper$class*/$init$($this: MySuper): Unit = {
            ()
          }
          };
          abstract trait MyTrait$class extends  { self: MyTrait =>
            def createdFilter($this: MyTrait): java.lang.String = "override this";
            override def filter($this: MyTrait): java.lang.String = $this.$asInstanceOf[MySuper]().filter().+($this.createdFilter());
        def /*MyTrait$class*/$init$($this: MyTrait): Unit = {
          ()
        }
        };
        final class TestingOutTraits$$anon$1 extends java.lang.Object with MySuper with MyTrait {
        override def filter(): java.lang.String = MyTrait$class.filter(TestingOutTraits$$anon$1.this);
        def start(): java.lang.String = MySuper$class.start(TestingOutTraits$$anon$1.this);
        override def createdFilter(): java.lang.String = "second part";
        def this(): anonymous class TestingOutTraits$$anon$1 = {
          TestingOutTraits$$anon$1.super.this();
          MySuper$class./*MySuper$class*/$init$(TestingOutTraits$$anon$1.this);
          MyTrait$class./*MyTrait$class*/$init$(TestingOutTraits$$anon$1.this);
          ()
        }
        }
3
som-snytt On

Alternatively,

trait MySuper extends Filtered {

  protected def filter: String = {
    "first part to->"
  }

  def start() = {
    filter
  }
}

trait Filtered {
  protected def filter: String
}
trait MyTrait extends Filtered {

  lazy val createdFilter = {
    "override this"
  }

  protected abstract override def filter: String = {
    super.filter + createdFilter
  }
}

then

  val nope = new MyTrait { }  // correctly DNC

and the OP

  val TestHandler = new MySuper with MyTrait {
    override lazy val createdFilter = {
      "second part"
    }
  }

http://www.artima.com/pins1ed/traits.html#12.5