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=]
我 运行 今天在 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=]