E009: Early Definitions Not Supported

This error is emitted when using early definitions (early initializers), which were a feature in Scala 2 but are no longer supported in Scala 3. Use trait parameters instead.

Earlier versions of Scala did not support trait parameters and "early definitions" (also known as "early initializers") were used as an alternative to initialize values before the superclass constructor runs.

In Scala 3, trait parameters provide a cleaner solution to this problem.


Example

trait Logging:
  val logFile: String
  println(s"Logging to $logFile")

class App extends { val logFile = "app.log" } with Logging

Error

-- Error: example.scala:5:18 ---------------------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                  ^
  |                  `extends` must be followed by at least one parent
-- [E009] Syntax Error: example.scala:5:46 -------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                                              ^^^^
  |         Early definitions are not supported; use trait parameters instead
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  | Earlier versions of Scala did not support trait parameters and "early
  | definitions" (also known as "early initializers") were used as an alternative.
  |
  | Example of old syntax:
  |
  | trait Logging {
  |   val f: File
  |   f.open()
  |   onExit(f.close())
  |   def log(msg: String) = f.write(msg)
  | }
  |
  | class B extends Logging {
  |   val f = new File("log.data") // triggers a NullPointerException
  | }
  |
  | // early definition gets around the NullPointerException
  | class C extends {
  |   val f = new File("log.data")
  | } with Logging
  |
  | The above code can now be written as:
  |
  | trait Logging(f: File) {
  |   f.open()
  |   onExit(f.close())
  |   def log(msg: String) = f.write(msg)
  | }
  |
  | class C extends Logging(new File("log.data"))
   -----------------------------------------------------------------------------
-- Error: example.scala:5:51 ---------------------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                                                   ^^^^^^^
  |                  end of toplevel definition expected but identifier found

Solution

// Use trait parameters instead of early definitions
trait Logging(logFile: String):
  println(s"Logging to $logFile")

class App extends Logging("app.log")
// Alternative: Use a lazy val to defer initialization
trait Logging:
  def logFile: String
  lazy val logger = s"Logging to $logFile"

class App extends Logging:
  val logFile = "app.log"