Lazy val not initialized in trait

609 views
Skip to first unread message

Edmondo Porcu

unread,
Jun 26, 2012, 8:30:15 AM6/26/12
to scala...@googlegroups.com
Dear all,
I have the folowing trait :

trait TickerCollection[T] {

val items: List[T]

val mapping: T => PublishDataTicker

lazy val publishedDataTickers = items.zip(items.map { mapping } )


}

trait SingleCurveAssociatedTickerCollection[T<:MyClass] extends TickerCollection[T]{

val curveType: CurveType

val mapping: T => PublishDataTicker = item => item.tickerFor(curveType)


}

When I mix this into a class, however, no bitmap field is generated for the lazy val, and it stays null forever producing a nullpointer exception.

Do you have any similar experience?
Best Regards

Edmondo Porcu



√iktor Ҡlang

unread,
Jun 26, 2012, 8:53:16 AM6/26/12
to Edmondo Porcu, scala...@googlegroups.com
Prefer def and lazy vals in traits over vars and vals.

Cheers,
--
Viktor Klang

Akka Tech Lead
Typesafe - The software stack for applications that scale

Twitter: @viktorklang

Som Snytt

unread,
Jun 26, 2012, 3:15:04 PM6/26/12
to Edmondo Porcu, scala...@googlegroups.com
Here is the example filled out, just for educational value.

Maybe your stack trace is more interesting, but it seems you can try again after blowing up in a lazy initializer, so it's not true that "it stays null forever."  (I guess you mean it blows up forever, too.)  Probably someone has mentioned that on SO.

package nullinit


trait TickerCollection[T] {
  val items: List[T]
  val mapping: T => PublishDataTicker
  lazy val publishedDataTickers = {
    println("I'm really trying!  Mapping is "+ mapping)
    items.zip(items.map { mapping } )
  }
}

trait CurveType
trait PublishDataTicker


trait SingleCurveAssociatedTickerCollection[T<:MyClass] extends TickerCollection[T] {
  val curveType: CurveType
  val mapping: T => PublishDataTicker = item => item.tickerFor(curveType)
}

trait ItemsProvider extends TickerCollection[MyClass] {
  val items: List[MyClass] = List(new MyClass)
  try {
    println("My tickers "+ publishedDataTickers)
  } catch { case t => println("Catch the wave."); t.printStackTrace() }
}

class MyClass {
  def tickerFor(t: CurveType): PublishDataTicker = new PublishDataTicker { }
}

class MyImpl extends ItemsProvider with SingleCurveAssociatedTickerCollection[MyClass] {
  val curveType = new CurveType { }
}

object Test {
  def main(args: Array[String]) {
    val x = new MyImpl
    println("My tickers2 "+ x.publishedDataTickers)

Josh Suereth

unread,
Jun 26, 2012, 3:23:53 PM6/26/12
to Som Snytt, Edmondo Porcu, scala...@googlegroups.com
You've run into the classic "constructor" hell that traits enable.

Trait initialization order *matters* and you're violating it here.


class MyImpl extends ItemsProvider with SingleCurveAssociatedTickerCollection[MyClass] { .. }

So, our order is -> ItemsProvider then SingleCurveAssociatedTickerCollectoin then MyImpl.

However, you're printing the value in ItemsProvider and *assigning* it in MyImpl.   Of course it's null, you haven't assigned it before you accessed it.   If you compile with -Xcheckinit it will add a bunch of bytecode that will issue warnings to such an effect.


If you want to fix it, change mapping to a *def* so it can delegate appropriately.

Even better, *don't* access the lazy val in the constructor.   Wait until the object is fully initialized, you'll get the result you want.


Finally, you can use early initializers instead, this may work, but I haven't tested it:

trait SingleCurveAssociatedTickerCollection[T<:MyClass] extends {
  val mapping: T => PublishDataTicker = item => item.tickerFor(curveType)
} with TickerCollection[T] {
  val curveType: CurveType
}

Josh Suereth

unread,
Jun 26, 2012, 3:26:42 PM6/26/12
to Som Snytt, Edmondo Porcu, scala...@googlegroups.com
Some REPL fun to demonstrate the point:

scala> trait X {
     |   val y : Int
     | }
defined trait X

scala> trait Z extends X { println(y) }
defined trait Z

scala> class A extends X with Z { val y = 1 }
defined class A

scala> trait Y extends { val y = 1 } with X
defined trait Y

scala> class B extends X with Y with Z
defined class B

scala> new A
0     <- O NOES DEFAULT VALUE!!!
res0: A = A@663c0737

scala> new B
1  <- early initializer SAVED US!
res1: B = B@78a40f0e

Som Snytt

unread,
Jun 26, 2012, 4:11:11 PM6/26/12
to Josh Suereth, Edmondo Porcu, scala...@googlegroups.com
Thanks, I should have added to the sample, "Do as Viktor and Josh say, this is not an example of what to do."

So, in case I google "trait initialization null bomb *$<!" in six months, note to self.

But by 2.11 we'll have scalac -explain:init-order, right?  (Assuming someone contributes it.)

Jason Zaugg

unread,
Jun 26, 2012, 5:27:09 PM6/26/12
to Josh Suereth, Som Snytt, Edmondo Porcu, scala...@googlegroups.com
On Tue, Jun 26, 2012 at 9:23 PM, Josh Suereth <joshua....@gmail.com> wrote:
> You've run into the classic "constructor" hell that traits enable.
>
> Trait initialization order *matters* and you're violating it here.

> Finally, you can use early initializers instead, this may work, but I
> haven't tested it:
>
> trait SingleCurveAssociatedTickerCollection[T<:MyClass] extends {
>   val mapping: T => PublishDataTicker = item => item.tickerFor(curveType)
> } with TickerCollection[T] {
>   val curveType: CurveType
> }

Which reminds me:

https://issues.scala-lang.org/browse/SI-2796
https://github.com/scala/scala/pull/777

-jason
Reply all
Reply to author
Forward
0 new messages