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"
In this article