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

Scala Abstract type members - inheritance and type bounds

我 运行 今天在 Scala 中遇到了一些 st运行ge 情况,同时我试图细化抽象类型成员的类型边界。

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

我试着准备了一个例子:

trait L
trait R

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

trait Right {
  type T <: R
}

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

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

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

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

但是如果我转换为 (Left with Right) 或模式匹配,我将无法再访问该成员。根据顺序,我从左侧或右侧获得类型边界,但不是两者的组合。

// 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

我知道 Left with Right 与 Right with Left 不同,但在这两种情况下都包含两种类型界限,那么为什么我只能让一个起作用?

谁能解释一下为什么会这样?

第二种类型的成员覆盖第一种。

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

在 "concrete" 中,您使用 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 中定义,或者通过类似于 shapeless [ 的机制在某些类型类中派生=13=]