2
votes

So I'm reading the Scala for the Impatient book and one of the examples it uses is a Logger trait that essentially prints a String to some stream. In the example, it has a trait ConsoleLogger (which extends Logger) that prints the message out to stdout and ShortLogger (also extends Logger) that simply truncates the string if the length is too long. To change the maximum length of ShortLogger the book proposes uses an anonymous subclass, something like:

val acct = new SavingsAccount with ConsoleLogger with ShortLogger {
  val maxLength = 20
}

Where ShortLogger is a trait with an abstract maxLength: Int field and SavingsAccount is defined as

class SavingsAccount extends Account with Logged { ... }

Which makes sense to me (kind of). I'm assuming the construction order is:

  1. Logger gets constructed first (since it is a super-trait of ConsoleLogger),
  2. ConsoleLogger
  3. ShortLogger
  4. Account
  5. SavingsAccount.
  6. Then we have the anonymous subclass construction where we define the abstract maxLength = 20.

However, later on in the book, it gives a new Logger sub-trait:

trait FileLogger extends Logger {
  val filename: String
  val out = new PrintStream(filename)
  def log(msg: String) { out.println(msg); out.flush() }
}

val acct = new SavingsAccont with FileLogger {
  val filename = "myapp.log" // Does not work
}

It says it does not work due to the construction order. They propose the modification:

val acct = new {
  val filename: "myapp.log"
} with SavingsAccount with FileLogger

However, this definition seems to be similar to the one above with maxLength, so what am I missing between the above example and the bottom example?

1

1 Answers

5
votes

Your construction order is entirely wrong. :-)

The first thing to be constructed is the class, then you go through the traits from left to right. It's a bit more complex to that -- there's linearization rules that deal with traits inherited multiple times, but that's about it.

So:

  1. Account
  2. Logged
  3. SavingsAccount
  4. ConsoleLogger
  5. ShortLogger
  6. Anonymous subclass

Now, to your question:

val acct = new SavingsAccont with FileLogger {
  val filename = "myapp.log" // Does not work
}

val acct = new {
  val filename: "myapp.log"
} with SavingsAccount with FileLogger

Note that the block of code comes first in the second example. That's known as early initialization, and what it does is to apply that initialization before anything else.