Suppose we want to determine how to accumulate results from some data:
case class Data(x: Int, y: Int)
We define a trait for this:
trait Accumulator { type R def add(acc: R, d: Data): R def zero: R }
And a simple implementation:
trait XAccumulator extends Accumulator { type R = Int def add(acc: Int, d: Data) = acc + dx def zero = 0 }
I would like to use the stackable-trait pattern to use several of these simple batteries:
trait TraceYAccumulator extends Accumulator { abstract override type R = (Seq[Int], super.R) // fails: // `abstract override' modifier not allowed for type members def add(acc: R, d: Data) = { val r = super.add(acc._2, d) (acc._1 :+ dy, r) } def zero = (Seq.empty[Int], super.zero) }
Apparently, I am not allowed to redefine an element of an abstract type. How to change the result type of overridden methods using the template of stackable attributes?
My second approach was to use type parameters:
trait Accumulator[R] { def add(acc: R, d: Data): R def zero: R } trait XAccumulator extends Accumulator[Int] { def add(acc: Int, d: Data) = acc + dx def zero = 0 }
But now it's getting really weird:
trait TraceYAccumulator[T] extends Accumulator[(Seq[Int], T)] { this: Accumulator[T] => def add(acc: (Seq[Int], T), d: Data): (Seq[Int], T) = { val r = this.add(acc._2, d) // fails: overloaded method value add with alternatives: // (acc: (Seq[Int], T),d: Data)(Seq[Int], T) <and> // (acc: _14695,d: Data)_14695 cannot be applied to (T, Data) (acc._1 :+ dy, r) } def zero: (Seq[Int], T) = (Seq.empty[Int], this.zero) }
Since the superclass class and the mixed class have the same method names (obviously), we cannot refer to the correct methods. My only use case for composition?
How can I add such operations?