Dziedziczenie i ponowne wykorzystanie kodu w cechach możliwych do układania w stosy

W tym uproszczonym eksperymencie chcę być w stanie szybko zbudować klasę z cechami stosu, które mogą raportować, jakie cechy zostały użyte do jej zbudowania. Przypomina mi to mocno wzór dekoratora, ale wolę mieć to zaimplementowane w czasie kompilacji niż w czasie wykonywania.

Przykład pracy z kodem nadmiarowym

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)
  }
}

Wykonywanie(new TraitTest with Moo with Quack).report(0) zgłosiłbym wtedy:

> 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 

Niestety, jest tam dużo zbędnego kodu, który sprawia, że ​​moje oko drży. Moja próba oczyszczenia prowadzi mnie do:

Nie działający przykład bez kodu nadmiarowego

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"
}

Kiedy znów wykonamy(new TraitTest with Moo with Quack).report(0), teraz widzimy:

> At depth 0, I make the sound 'Quack'
  At depth 1, we've reached the end of our recursion

Pytanie 1: Gdzie poszła linia do „Moo”?

Zgaduję, że Scala tylko widzioverride def report(d: Int) raz, a zatem raz umieszcza go w łańcuchu dziedziczenia. Chwytam się słomy, ale jeśli tak jest, jak mogę to obejść?

Pytanie 2: W jaki sposób każda konkretna cecha może zapewnić wyjątkowysound?

Po rozwiązaniu pierwszego pytania zakładałbym wyniki wykonywania(new TraitTest with Moo with Quack).report(0) wyglądałby jak poniżej, ze względu na sposób dziedziczeniasound pracowałbym.

> 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  

Jak możemy to zrobić, aby każda cecha używałasound określony w jego implementacji?

questionAnswers(3)

yourAnswerToTheQuestion