10


我在 scala 中有一个编译器错误,我不知道它指的是什么:
假设这些声明:

trait Abstract {
  type MyType
}
trait AInner
trait A extends Abstract{
  type MyType <: AInner
}
trait BInner {
  def bMethod : Int
}
trait B extends Abstract with A{
  override type MyType <: BInner with A#MyType
}
我在这里(在 trait 中B)想要实现的是进一步限制在 中MyType声明的类型Abstract,因此任何类型的值都MyType必须扩展MyTypemixin 树中的所有 s。

编译器给了我这个消息(如标题): 类型 MyType 是易失类型;不能覆盖具有非易失性上限的类型。我知道,由于类型结合,这里发生了类型波动with A#MyType,错误的一部分:具有非易失性上限的类型可能是指类型声明type MyType <: AInner,其中AInner不是抽象类型,因此是非易失性的。

为什么我做不到?有没有办法,如何实现我的目标?

4

3 回答 3

12

在编译器中删除这个检查让我们能够发现不健全的可能性。

diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 37a7e3c..78a8959 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -5128,8 +5128,7 @@ trait Typers extends Adaptations with Tags {

       def typedSelectFromTypeTree(tree: SelectFromTypeTree) = {
         val qual1 = typedType(tree.qualifier, mode)
-        if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual1)
-        else typedSelect(tree, qual1, tree.name)
+        typedSelect(tree, qual1, tree.name)
       }

       def typedTypeBoundsTree(tree: TypeBoundsTree) = {

然后,从编译器测试用例中运行代码以检查 volatile 类型的非法类型选择:

scala> class A; class B extends A
defined class A
defined class B

scala> trait C {
     |   type U
     |   trait D { type T >: B <: A }
     |   val y: (D with U)#T = new B
     | }
defined trait C

scala> class D extends C {
     |   trait E
     |   trait F { type T = E }
     |   type U = F
     |   def frob(arg : E) : E = arg
     |   frob(y)
     | }
defined class D

scala> new D
java.lang.ClassCastException: B cannot be cast to D$E

据我了解,问题源于 Scala 没有真正的交集类型。

scala> type A = { type T = Int }
defined type alias A

scala> type B = { type T = String }
defined type alias B

scala> "": (A with B)#T
res16: String = ""

scala> 0: (A with B)#T
<console>:37: error: type mismatch;
 found   : Int(0)
 required: String
              0: (A with B)#T
              ^

如果对依赖对象类型 (DOT)的研究取得成果,这种情况在未来可能会发生变化。

于 2013-04-21T12:56:54.767 回答
1

你可以重写trait B(稍后更多关于你的目标,我认为这有点不同)

trait B extends A {
  type MyType <: BInner with AInner
}

这是完全有道理的。type 的值B#MyType可以被视为 aBInner或 a AInner

您无需重复Abstract,因为A它已经是Abstract. 您不必编写override,因为这对于类型声明是隐含的。所以问题是为什么A#MyType不作为AInner

这是 scala 语言规范对 volatile 类型的说明。

3.6 挥发性类型

类型易变性近似于类型参数或类型的抽象类型实例没有任何非空值的可能性。正如 (§3.1) 中所解释的, volatile 类型的值成员不能出现在路径中。如果一个类型属于以下四个类别之一,则该类型是易失性的: 如果满足以下两个条件之一,则具有 ... 和 Tn {R} 的复合类型 T1 是易失性的。1. T2, ..., Tn 之一是类型参数或抽象类型,或者 2. T1 是抽象类型,并且对于 j > 1 的细化 R 或类型 Tj 为复合类型贡献了一个抽象成员,或 3. T1, ..., Tn 之一是单例类型。这里,如果 S 包含一个也是 T 成员的抽象成员,则类型 S 将抽象成员贡献给类型 T 。如果 R 包含一个抽象声明,它也是 T 的成员,则细化 R 将抽象成员贡献给类型 T。如果类型指示符是 volatile 类型的别名,或者如果它指定具有 volatile 类型作为其上限的类型参数或抽象类型,则该类型指示符是 volatile。如果路径 p 的基础类型是易失的,则单例类型 p.type 是易失的。如果 T 是易失的,则存在类型 T forSome {Q } 是易失的。

规范中提到的其他重要项目是关于抽象类型覆盖:

另一个限制适用于抽象类型成员:具有 volatile 类型(第 3.6 节)作为其上限的抽象类型成员不能覆盖没有 volatile 上限的抽象类型成员。

编译器错误是:

error: overriding type MyType in trait A with bounds <: AInner;
type MyType is a volatile type; cannot override a type with non-volatile upper bound

这与规范一致。BInner with A#MyType是易变的。在此之前MyType有一个非易失性的 as Any

问题是 scala 类型系统中的类型必须具有唯一的含义。抽象类型可以被认为是声明被推迟到子类的类型。因此,在抽象类型仍然是抽象的时候声明它的值是没有问题的。另一方面,如果我们有一个类似的类型BInner with A#MyType,这个类型可能有几个含义。它被称为 volatile 并且具有这种类型的非 null 值是没有意义的,因为它可以具有与实例化MyType抽象类型的子类一样多的类型。为简化起见,我们可以将 volatile 类型视为不是子类型的类型Any(而 volatile 视为子类型Any)。因此,我们有一个编译器提到的矛盾。

回到你的目标,你说的

我在这里(在特征 B 中)试图实现的是进一步限制在 Abstract 中声明的 MyType 类型,因此 MyType 类型的任何值都必须扩展 mixin 树中的所有 MyType。

由于这样的内在特征,您可以实现这一目标。

trait Abstract {
  type MyType
}
trait B extends Abstract {
  trait MyType {
    def bMethod : Int
  }
}
trait A extends B {
  trait MyType extends super.MyType {
  }
}

嗯,我希望这有点你在找什么。

于 2013-04-20T22:18:45.010 回答
0

这有什么问题?

trait B extends Abstract with A {
  override type MyType <: BInner with AInner
}

在 的任何实现中trait BMyType将始终与从 中看到的类型相同trait A,因此单独设置上限没有任何意义。

如果在上面的代码中,trait B如果更改 in 的界限,您将不得不重写,这让您感到困扰,请trait A使用:

trait A extends Abstract{
  type ABound = AInner
  type MyType <: AInner
}
trait B extends Abstract with A {
  override type MyType <: BInner with ABound
}
于 2013-04-19T22:26:00.543 回答