4

假设我们要定义如何在某些数据上累积结果的方法:

case class Data(x: Int, y: Int)

为此,我们定义了一个特征:

trait Accumulator {
  type R   
  def add(acc: R, d: Data): R
  def zero: R
}

还有一个简单的实现:

trait XAccumulator extends Accumulator {
  type R = Int
  def add(acc: Int, d: Data) = acc + d.x
  def zero = 0
}

我想使用 stackable-trait 模式来使用多个这些简单的累加器:

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

显然我不允许重写抽象类型成员。如何使用可堆叠特征模式更改覆盖方法的结果类型?

我的第二种方法是使用类型参数:

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
}

但现在它变得非常奇怪:

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

由于超类和混入类具有相同的方法名称(显然),我们无法引用正确的方法。我唯一的选择是使用组合吗?

我怎样才能堆叠这样的操作?

4

1 回答 1

1

第一种方法有一个基本的理论问题:

假设我们被允许编写这些特征。然后考虑以下几点:

val acc = new XAccumulator with TraceYAccumulator
val xacc = acc: XAccumulator

既然xacc是一个XAccumulator,我们就知道了x.R =:= Int。哎呀。

第二种方法有一个实现问题:您不能从相同特征的不同实例继承。

illegal inheritance; anonymous class $anon inherits different type instances of trait Accumulator: Accumulator[Int] and Accumulator[(Seq[Int], Int)]

因此,组合(或者可能是一些类型类黑客)似乎是唯一的选择。

于 2014-03-20T08:03:18.540 回答