2013-04-08 12 views
9


ho un errore del compilatore in scala e non so che cosa si riferiscono a:
Assumere queste dichiarazioni: non può escludere un tipo con non volatile limite superiore

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 
} 
Quello che sto cercando di realizzare qui (nel tratto B) è necessario limitare ulteriormente il tipo MyType dichiarato in Abstract, quindi qualsiasi valore di tipo MyType deve estendere tutti gli MyType s nell'albero del mix.

Il compilatore mi sta dando questo messaggio (come nel titolo): tipo MyType è un tipo volatile; non è possibile sovrascrivere un tipo con limite superiore non volatile. Intendo che tipo volatilità qui accada di tipo congiunzione with A#MyType, la parte dell'errore: tipo a non volatile limite superiore probabilmente si riferisce alla dichiarazione di tipo type MyType <: AInner, dove AInner non è un tipo astratto quindi non volatile.

Perché non posso farlo? C'è un modo, come raggiungere il mio obiettivo?

risposta

10

La rimozione di questo controllo nel compilatore ci consente di far luce sul potenziale di non-integrità.

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

Poi, l'esecuzione del codice da un banco di prova compilatore per la selezione del tipo illegale per i tipi di volatili:

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 

Da quanto ho capito, il problema deriva dal fatto che la Scala non ha vera tipi di intersezione.

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 
      ^

Questo potrebbe cambiare in futuro, se la ricerca in Dependent Object Types (DOT) porta i suoi frutti.

+1

Perché anche i membri del metodo astratto sono vietati nei tipi non volatili? – Blaisorblade

0

Cosa c'è di sbagliato in questo?

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

In ogni realizzazione di trait B, MyType sarà sempre dello stesso tipo, come risulta dalla trait A, così superiore delimitano per sé non ha alcun senso.

Se nel pezzo superiore del codice, ti dà fastidio che si dovrà riscrivere trait B se si modifica la rilegato in trait A, uso:

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

Questa sembra essere una buona soluzione, ma la cosa che ancora non capisco è il motivo per cui è necessario. Il mio commento sulla risposta di Leo è lo stesso qui ... Le specifiche sono abbastanza chiare su dove possono essere usati i tipi volatili, ma non sono così informativi su ciò che significano. L'unico suggerimento è che "approssimano la possibilità che un tipo non abbia valori non nulli", ma suppongo che sia una spiegazione fuorviante in questo contesto. qualche idea? – mergeconflict

1

È possibile riscrivere trait B con (più avanti sulla tua obiettivo, che, penso, è un po 'diverso)

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

E questo ha perfettamente senso. Un valore di tipo B#MyType può essere visto come BInner o AInner.

Non è necessario ripetere Abstract perché A è già una sottoclasse di Abstract. Non è necessario scrivere override poiché ciò è implicito per una dichiarazione di tipo. Quindi la domanda è: perché A#MyType non funziona come AInner?

Ecco cosa dice lo scala linguaggio spec. Sui tipi volatili.

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.

Altro importante elemento di cui parla la specifica è di circa tipo prioritario astratto:

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.

L'errore del compilatore è:

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 

Ciò è coerente con la specifica. BInner with A#MyType è volatile. Prima che MyType avessero un non volatile come Any.

Il problema è che un tipo nel sistema di tipo scala deve avere un significato univoco. Un tipo astratto può essere pensato come un tipo la cui dichiarazione è rinviata ad una sottoclasse.Quindi non c'è alcun problema nel dichiarare valori di un tipo astratto quando è ancora astratto. D'altra parte, se abbiamo un tipo come BInner with A#MyType, questo tipo potrebbe avere diversi significati. Si chiama volatile e non ha senso avere un valore non nullo di questo tipo, in quanto potrebbe avere tanti tipi di sottoclassi che istanziano il tipo astratto MyType. Per semplificare le cose, potremmo pensare a un tipo volatile come un tipo non essendo un sottotipo di Any (e volatile come sottotipo Any). Abbiamo quindi una contraddizione che il compilatore menziona.

Tornando al vostro obiettivo, che lei ha affermato come

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.

È possibile raggiungere questo obiettivo grazie a caratteristiche interne di questo tipo.

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

Beh, spero che questo sia ciò che stai cercando.

+0

Non credo che la tua interpretazione "perché" A # MyType' è astratta, "Vincolare con A # MyType" è bizzarra "è corretta. È perfettamente valido scrivere, ad esempio, "AInner with Abstract # MyType" anche se sembra ugualmente bizzarro. Le specifiche sono abbastanza chiare su dove possono essere usati i tipi volatili, ma non sono così informativi su ciò che intendono (cioè, perché le regole che li riguardano sono necessarie per la solidità). L'unico suggerimento è che essi "approssimano la possibilità che un tipo non abbia valori non nulli", e non penso che il tuo argomento lo capisca. – mergeconflict

+0

@mergeconflict Ho rielaborato l'argomento e spero che ora sia molto più chiaro. – Leo

Problemi correlati