Search code examples
scalatypeclasscake-pattern

Scala - Cake Pattern + Typeclasses + Implementations needing constructor parameter


Here is a bit of code I've distilled down as much as I can:

trait CakeLayer[A] extends {
  // typeclass hack to make it play nice with traits
  implicit def requireTypeclass: MyTypeclass[A]
  val typeclassInVal = requireTypeclass

  /* other stuff */
}

class FooImpl

object FooImpl {
  implicit object FooImplIsTypeclass extends MyTypeclass[FooImpl]
}

// This works perfectly
class Foo extends CakeLayer[FooImpl] {
  override def requireTypeclass = implicitly
}

// This gives me NullPointerException
// Occurs in a function of my "Bar" that contains an anonymous function
// which uses typeclassInVal. it is the first access to that val
// probably due to the parameter in the typeclass constructor?
class BarImpl(x: Int)

object BarImpl {
  class BarImplIsTypeclass(x: Int) extends MyTypeclass[BarImpl]
}

class Bar(x: Int) extends CakeLayer[BarImpl] {
  val typeclass = new BarImpl.BarImplIsTypeclass(x)
  override def requireTypeclass = typeclass
}

Solution

  • Simply variable initialization order, which start with ancestors.

    First, typeclassInVal in ancestor trait is initialized. To do that, requireTypeclass is called. It is overriden in Bar and access val typeclass, not yet initialized, so null at this point. So typeclassInVal is initialized to null once and for all, and you get an NPE the first time it is used.

    Simple workaround could be not having a val, but just a def in the ancestor trait, or having a lazy val.