2013-04-08 50 views
9


我有一個編譯器錯誤階型,我不知道這是什麼參考:
假設這些聲明:不能覆蓋使用非易失性上限

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 
} 
我試圖在這裏實現(在特徵 B中)是爲了進一步限制在 Abstract中聲明的類型 MyType,所以任何類型的值 MyType都必須擴展混合樹中的所有 MyType

編譯器給我這個信息(如標題): 類型MyType是一個易失性類型;不能覆蓋具有非易失性上限的類型。我明白,型波動正在發生的事情,因爲類型一起選擇with A#MyType這裏,錯誤的部分:型與非揮發性上限大概是指類型聲明type MyType <: AInner,其中AInner不是一個抽象的類型。因此非易揮發。

爲什麼我不能這樣做?有沒有辦法,如何實現我的目標?

回答

10

在編譯器中刪除此檢查可讓我們發現潛在的不穩定性。

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) = { 

然後,從編譯器測試用例非法選型揮發性類型運行代碼:

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 
      ^

這可能在未來改變,如果研究Dependent Object Types (DOT)掛果。

+1

爲什麼在非易失性類型中禁止抽象方法成員? – Blaisorblade 2014-07-01 23:03:45

0

這是什麼問題?

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

在任何實現trait BMyType總是會從trait A看到了同樣的類型,因此其本身上,包圍它沒有任何意義。

如果在上一段代碼,它困擾你,你就得重寫trait B,如果你改變裝訂在trait A,使用:

trait A extends Abstract{ 
    type ABound = AInner 
    type MyType <: AInner 
} 
trait B extends Abstract with A { 
    override type MyType <: BInner with ABound 
} 
+0

這似乎是一個很好的解決方案,但我仍然不明白的是爲什麼它是必要的。我對Leo的回答的評論在這裏是一樣的......關於_where可能會被使用的規範很清楚,但並不能說明他們的意思。唯一的提示就是它們「近似於一個類型沒有任何非空值的可能性」,但我猜這在這種情況下是一個誤導性的解釋。有什麼想法嗎? – mergeconflict 2013-04-21 00:14:05

1

以後,您可以改寫trait B與(更多關於你的目標,我認爲它有點不同)

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

而這是總的意義。類型B#MyType的值可以看作是BInnerAInner

您不需要重複Abstract,因爲A已經是Abstract的子類。您不必編寫override,因爲它對於類型聲明是隱含的。所以問題是爲什麼A#MyType不能作爲AInner工作?

以下是scala語言規範中關於volatile類型的說明。在規範中提到的

3.6 Volatile Types

Type volatility approximates the possibility that a type parameter or abstract type instance of a type does not have any non-null values. As explained in (§3.1), a value member of a volatile type cannot appear in a path. A type is volatile if it falls into one of four categories: A compound type T1 with ... with Tn {R } is volatile if one of the following two conditions hold. 1. One of T2, ..., Tn is a type parameter or abstract type, or 2. T1 is an abstract type and and either the refinement R or a type Tj for j > 1 contributes an abstract member to the compound type, or 3. one of T1, ..., Tn is a singleton type. Here, a type S contributes an abstract member to a type T if S contains an abstract member that is also a member of T . A refinement R contributes an abstract member to a type T if R contains an abstract declaration which is also a member of T . A type designator is volatile if it is an alias of a volatile type, or if it designates a type parameter or abstract type that has a volatile type as its upper bound. A singleton type p.type is volatile, if the underlying type of path p is volatile. An existential type T forSome {Q } is volatile if T is volatile.

其他重要的項目是關於抽象類型壓倒一切:

Another restriction applies to abstract type members: An abstract type member with a volatile type (§3.6) as its upper bound may not override an abstract type member which does not have a volatile upper bound.

編譯器錯誤是:

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具有非易失性,如Any

問題是scala類型系統中的類型必須具有唯一的含義。抽象類型可以被認爲是一種聲明被推遲到一個子類的類型。因此,在抽象類型抽象時聲明抽象類型的值沒有問題。另一方面,如果我們有類似BInner with A#MyType的類型,這種類型可能有幾個含義。它被稱爲volatile,並且具有此類型的非空值是沒有意義的,因爲它可以具有與實例化抽象類型的子類一樣多的類型。爲了簡化事情,我們可以將揮發性類型看作不是Any的子類型(並且易失性是子類型Any)。因此,我們有一個編譯器提到的矛盾。

再回到你的目標,你說作爲

What I'm trying to achieve here(in trait B) is to further restrict the type MyType declared > in Abstract, so any value of type MyType must extend all the MyTypes in the mixin tree.

你可以做到這一點要歸功於內部特質這樣。

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

那麼我希望這有點你在找什麼。

+0

我不認爲你的解釋是「因爲'A#MyType'是抽象的,'BInner with A#MyType'是怪異的」是正確的。例如,編寫「AInner with Abstract#MyType」是完全有效的,儘管它看起來同樣奇怪。關於_where可以使用volatile類型的規範非常明確,但不是關於它們的含義的信息(也就是說,爲什麼關於它們的規則對於正確性來說是必需的)。唯一的提示就是它們「近似於一個類型沒有任何非空值的可能性」,我認爲你的論證不能捕獲這個值。 – mergeconflict 2013-04-21 00:06:22

+0

@mergeconflict我重新闡述了這個觀點,並希望現在更清楚。 – Leo 2013-04-21 14:59:14