Reputation: 14842
Suppose we want to define ways how to accumulate results over some data:
case class Data(x: Int, y: Int)
We define a trait to do so:
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 + d.x
def zero = 0
}
I would like to use the stackable-trait pattern to use multiple of these simple accumulators:
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 :+ d.y, r)
}
def zero = (Seq.empty[Int], super.zero)
}
Apparently I am not allowed to override an abstract type member. How can I alter the result type of the overridden methods using the stackable trait pattern?
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 + d.x
def zero = 0
}
But now it becomes really strange:
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 :+ d.y, r)
}
def zero: (Seq[Int], T) = (Seq.empty[Int], this.zero)
}
Since the super-class and the mixed-in class have the same method names (obviously), we cannot refer to the correct methods. Is my only option to use composition?
How can I stack such kind of operations?
Upvotes: 4
Views: 859
Reputation: 14842
The first approach has a fundamental theoretical problem:
Suppose we are allowed to write these traits. Then consider the following:
val acc = new XAccumulator with TraceYAccumulator
val xacc = acc: XAccumulator
Since xacc
is an XAccumulator
, we know that x.R =:= Int
. Ooops.
The second approach has an implementation problem: You cannot inherit from different instances of the same trait.
illegal inheritance; anonymous class $anon inherits different type instances of trait Accumulator: Accumulator[Int] and Accumulator[(Seq[Int], Int)]
Therefore, composition (or maybe some typeclass hackery) seems to be the only option.
Upvotes: 1