Scala 抽象类型成员 - 继承和类型边界



今天我在 Scala 中遇到了一些奇怪的情况,当我试图优化抽象类型成员上的类型边界时。

我有两个特征,它们定义类型成员的边界并将它们组合到一个具体的类中。这工作正常,但是当与特征组合匹配/铸造时,两个 TypeBounds 中只有一个是"活跃的",我很难理解为什么......

我试图准备一个例子:

trait L
trait R
trait Left {
  type T <: L
  def get: T
}
trait Right {
  type T <: R
}

现在,如果我将这两个特征结合在一个具体的类中

val concrete = new Left with Right {
  override type T = L with R
  override def get: T = new L with R {}
}

我可以按预期通过获取访问我的会员

// works fine
val x1: L with R = concrete.get

但是如果我投射到(左与右)或模式匹配,我将无法再访问该成员。根据顺序,我从左或右获得类型边界,而不是两者的组合。

// can only access as R, L with R won't work
val x2: R = concrete.asInstanceOf[Left with Right].get
// can only access as L, L with R won' work
val x3: L = concrete.asInstanceOf[Right with Left].get

我知道左与右与右不是一回事,但在这两种情况下都包含两个类型边界,那么为什么我只能得到一个工作?

谁能阐明为什么会发生这种情况?

第二个类型成员将覆盖第一个类型成员。

trait L
trait R
trait Left {
  type T <: L
  def get: T
}
trait Right {
  type T <: R
}
object X {
  type LR = Left with Right // Right#T overrides Left#T, LR#T is now <: R
  type RL = Right with Left // Left#T overrides Right#T, LR#T is now <: L
  val concrete = new Left with Right {
    override type T = L with R
    override def get: T = new L with R {}
  }
  // ok
  val r: R = concrete.asInstanceOf[LR].get
  val l: L = concrete.asInstanceOf[RL].get
  // ok
  implicitly[LR#T <:< R]
  implicitly[RL#T <:< L]
  // doesn't compile, LR#T is a subclass of R because Right#T overrides Left#T
  implicitly[LR#T <:< L]
  // doesn't compile, RL#T is a subclass of L because Left#T overrides Right#T
  implicitly[RL#T <:< R]
}

在"具体"中,你用 L with R 覆盖类型成员,但是当你把它转换为Left with Right时,你会失去这种细化,T 变成 _ <:L 或 _ <:R,具体取决于特征的顺序。

由于可以覆盖类型成员,因此如果向上转换(例如,向上转换为 LR 或 RL),则会丢失在混凝土中应用的细化。你可以说具体同时是 RL 和 LR,但是当你把它转换为 LR 或 RL 时,你会丢失你在另一个中拥有的信息

除了TrustNoOne的答案之外,我可以建议遵循一些限制的解决方法。您可以设计自己的类型组合器,而不是with来克服类型重写。

  trait L
  trait R
  trait Base {
    type T
    def get: T
  }
  trait Nil extends Base{
    type T = Any
  }
  trait ~+~[X[_ <: Base] <: Base, Y[_ <: Base] <: Base] extends Base {
    type T = Y[X[Nil]]#T
  }
  trait Left[B <: Base] extends Base {
    type T = B#T with L
  }
  trait Right[B <: Base] extends Base {
    type T = B#T with R
  }
  val concrete = new (Left ~+~ Right) {
    def get: T = new L with R {}
  }
  val x1: L with R = concrete.get
  val x2: R = concrete.asInstanceOf[Left ~+~ Right].get
  val x3: L = concrete.asInstanceOf[Right ~+~ Left].get

这段代码现在可以成功编译,但请注意,我们不能在新类型中合并组合类型的命名空间,因此所有已知方法都应该在 Base 中定义或通过类似于无形HList的机制在某个类型类中派生

最新更新