2

Why does adding a proxy trait work and direct inheritance fail?

I've created a runnable project over on github

if you clone the repo and run sbt console followed by com.stackoverflow.question.main.Question.main(Array.empty[String]), you should end up with the output:

this-is-the-host-name
java.lang.NullPointerException
  at net.ceedubs.ficus.FicusConfig$class.as(FicusConfig.scala:9)
  at net.ceedubs.ficus.SimpleFicusConfig.as(FicusConfig.scala:16)
  at com.stackoverflow.question.AppConfig$class.conn(Config.scala:12)
  at com.stackoverflow.question.main.BadDatabaseService$.conn(package.scala:12)
  at com.stackoverflow.module.HostConnector$class.getHost(Database.scala:25)
  at com.stackoverflow.question.main.BadDatabaseService$.getHost(package.scala:12)
  at com.stackoverflow.module.DatabaseConnector$class.$init$(Database.scala:18)
  at com.stackoverflow.question.main.BadDatabaseService$.<init>(package.scala:12)
  at com.stackoverflow.question.main.BadDatabaseService$.<clinit>(package.scala)
  at com.stackoverflow.question.main.Question$.main(App.scala:8)
  ... 43 elided

the first line this-is-the-host-name is generated from the package object DatabaseService while the NullPointerException is generated by the BadDatabaseService package object. Both are identical except that the DatabaseService inherits from a proxy trait, while BadDatabaseService inherits directly from the trait. The relevant code is here for convenience and can also be seen in the github repo.

package.scala:

package com.stackoverflow.question

import com.stackoverflow.module._
import com.stackoverflow.question.AppConfig

package main {
  trait DataConfig extends ModuleConfig {
    def conn = AppConfig.conn
  }

  object DatabaseService extends TableFunctions with HostConnector with DataConfig   // This works with the proxy
  object BadDatabaseService extends TableFunctions with HostConnector with AppConfig // This fails with NullPointerException, it's the ConfigFactory.load that returns null
}

Config.scala:

package com.stackoverflow.question

import com.stackoverflow.module.{ ModuleConfig, ModuleConfiguration }
import com.typesafe.config.{ Config, ConfigFactory }
import net.ceedubs.ficus.Ficus._

object AppConfig extends AppConfig

trait AppConfig extends ModuleConfig {

  def config: Config = ConfigFactory.load()
  def conn = config.as[ModuleConfiguration]("stackoverflow.conn")

  val name = config.as[String]("stackoverflow.name")
}

Please help me understand why I can't inherit directly from AppConfig and why ConfigFactory.load returns null when AppConfig is inherited but not when it's co-object is called via proxy in inherited through that.

Alexander Kahoun
  • 2,458
  • 24
  • 36
  • Not marking as a duplicate because the question is different, but the answers are the same as in https://stackoverflow.com/questions/33248520/scala-class-using-mutable-var-update-in-overriding-method/33266720#33266720 – Alexey Romanov Oct 22 '15 at 17:02
  • Ha. http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html "no vals in traits" is a thing. – som-snytt Oct 22 '15 at 17:06
  • @AlexeyRomanov even if I change the def config to val config and override it with an early initializer, object BadDatabaseService extends { override val config = ConfigFactory.load } with TableFunctions with HostConnector with AppConfig, I get the same result. – Alexander Kahoun Oct 22 '15 at 17:59
  • I added branches for both of these suggestions and neither works, same error is received. See https://github.com/leftofnull/so-inheritance/tree/early-init or https://github.com/leftofnull/so-inheritance/tree/no-vals – Alexander Kahoun Oct 22 '15 at 18:13
  • 1
    It looks like the val which is actually the problem is `implicit val moduleConfigReader`. Can you try applying the solutions to it instead? – Alexey Romanov Oct 22 '15 at 18:45

1 Answers1

1

One additional solution that wasn't suitable for the linked question is to make AppConfig.name a lazy val.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • that yields the same results, https://github.com/leftofnull/so-inheritance/tree/lazy-vals – Alexander Kahoun Oct 22 '15 at 18:15
  • okay, per your comment above, changing it to: implicit lazy val moduleConfigReader: ValueReader resolved it. Thank you! https://github.com/leftofnull/so-inheritance/tree/lazy-implicit – Alexander Kahoun Oct 22 '15 at 19:59