In this simplified experiment, I want to be able to quickly build a class with stackable traits that can report on what traits were used to build it. This reminds me strongly of the decorator pattern, but I'd prefer to have this implemented at compile time rather than at runtime.
Working Example with Redundant Code
class TraitTest {
def report(d: Int) : Unit = {
println(s"At depth $d, we've reached the end of our recursion")
}
}
trait Moo extends TraitTest {
private def sound = "Moo"
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '$sound'")
super.report(d+1)
}
}
trait Quack extends TraitTest {
private def sound = "Quack"
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '$sound'")
super.report(d+1)
}
}
Executing (new TraitTest with Moo with Quack).report(0)
would then report:
> At depth 0, I make the sound 'Quack'
At depth 1, I make the sound 'Moo'
At depth 2, we've reached the end of our recursion
Unfortunately, there's a lot of redundant code in there that makes my eye twitch. My attempt at cleaning it up leads me to:
Non-working Example without Redundant Code
class TraitTest {
def report(d: Int) : Unit = {
println(s"At depth $d, we've reached the end of our recursion")
}
}
abstract trait Reporter extends TraitTest {
def sound : String
override def report(d: Int) : Unit = {
println(s"At depth $d, I make the sound '${sound}'")
super.report(d+1)
}
}
trait Moo extends Reporter {
override def sound = "Moo"
}
trait Quack extends Reporter{
override def sound = "Quack"
}
When we once again execute (new TraitTest with Moo with Quack).report(0)
, we now see:
> At depth 0, I make the sound 'Quack'
At depth 1, we've reached the end of our recursion
Question 1: Where did the line for 'Moo' go?
I'm guessing that Scala only sees override def report(d: Int)
the one time, and therefore only puts it in the inheritance chain once. I'm grasping at straws, but if that's the case, how can I work around that?
Question 2: How can each concrete trait supply a unique sound
?
After solving the first question, I would assume the results of executing (new TraitTest with Moo with Quack).report(0)
would look something like the following, due to how the inheritance of sound
would work.
> At depth 0, I make the sound 'Quack'
At depth 1, I make the sound 'Quack'
At depth 2, we've reached the end of our recursion
How can we make it so that each trait uses the sound
specified in it's implementation?
See Question&Answers more detail:
os