2013-04-08 24 views
9


Ben bir derleyici hatası olan bir tür geçersiz kılınamaz ve bunu ifade etmez bilmiyorum:
bu bildirimleri varsayalım: Burada elde çalıştığım şey Scala içinde uçucu olmayan üst sınır

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 özelliğinde), Abstract'da bildirilen MyType türünü daha fazla kısıtlamaktır, böylece MyType türündeki herhangi bir değer, karpuz ağacındaki tüm MyType s değerini uzatmalıdır.

derleyici (başlık gibi) Bana bu mesajı veriyor: tip MyType uçucu bir türüdür; uçucu olmayan üst sınırı olan bir türü geçersiz kılmaz. Bunun tip volatilite çünkü tipi conjuction with A#MyType burada oluyor, anlamak hatanın kısmı: ile tip uçucu olmayan üst sınır muhtemelen AInner böylece olmayan soyut bir tip değildir tür bildirimi type MyType <: AInner, ifade eder uçucu.

Neden yapamıyorum? Amacım nasıl elde edilir?

cevap

10

Bu denetimi derleyicide kaldırmak, olası bir bozulma olasılığına ışık tutmamızı sağlar.

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

Sonra uçucu türleri için yasadışı tipi seçimi için bir derleyici test davadan kod çalıştıran:

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 

Anladığım kadarıyla, mesele Scala gerçek yok olmasından kaynaklanıyor kesişim türleri. Dependent Object Types (DOT) araştırmalar meyve verir eğer

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 
      ^

Bu, gelecekte değişebilir.

+1

Ayrıca, uçucu olmayan tiplerde soyut yöntem üyeleri de yasaklanmıştır? – Blaisorblade

0

Bunun nesi yanlış? trait B herhangi gerçekleştirilmesinde

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

, MyType hep trait A bakıldığı zaman aynı tip olacak, bu yüzden tek başına bunu üst sınırlayıcı herhangi bir anlam ifade etmiyor.

kod üst parça halinde, bu size, trait A bağlanmış kullanımını değiştirirseniz trait B yeniden yazmak gerekecek size rahatsız ediyorsa:

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

Bu iyi bir çözüm gibi görünüyor, ancak hala anlamadığım şey, bunun neden gerekli olduğudur. Leo'nun cevabı hakkındaki yorumum da burada aynı ... Spesifikasyonlar nerede olduğu hakkında oldukça açık, ancak ne demek istedikleri hakkında bilgilendirici değil. Tek ipucu, "bir türün sıfır olmayan değerlere sahip olma olasılığını yaklaşık olarak tahmin etmeleridir", ancak sanırım bu bağlamda yanıltıcı bir açıklama var. Düşüncesi olan var mı? – mergeconflict

1

Yaklaşık sonrası ile trait B (daha yazabiliriz senin amaç, sanırım ki, farklı bir bit)

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

Ve bu toplam mantıklı. B#MyType tipi bir değer, BInner veya AInner olarak görülebilir.

zaten Abstract'un bir alt sınıfı olduğundan Abstract yinelemenize gerek yoktur. Bu tür bir bildirim için örtük olduğu için override yazmanız gerekmez. Bu yüzden soru A#MyType neden AInner olarak çalışmıyor?

Scala dilinin özellikleri, uçucu türleri hakkında ne diyor. spec tarafından bahsedilen

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.

Diğer önemli öğe soyut tip Burada üste hakkındadır:

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.

derleyici hatadır:

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 

Bu spec ile tutarlıdır. BInner with A#MyType uçucudur. Bundan önce MyType, Any olarak uçucu değildir.

madde scala tipi sisteminde bir tür eşsiz anlama sahip olmasıdır. Bir soyut tip, bir alt sınıfa deklare edilen bir tip olarak düşünülebilir.Bu nedenle, soyut olduğu zaman soyut bir türün değerlerini beyan etmekte sorun yoktur. Diğer yandan, BInner with A#MyType gibi bir türümüz varsa, bu türün birkaç anlamı olabilir. Uçucu olarak adlandırılır ve MyType soyut tipini örnekleyen alt sınıflar kadar çok türde olabileceğinden, bu türden boş olmayan bir değere sahip olma mantıklı değildir. Bazı şeyleri basitleştirmek için, Any alt tipi olmayan (ve Any alt türü olan uçucu) tip olarak bir uçucu tür düşünebiliriz. Bu nedenle derleyicinin bahsettiği bir çelişki var.

Böyle iç özellikleri için bu sayesinde elde edebilirsiniz

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.

gibi ifade hedefinize, geri dönersek.
trait Abstract { 
    type MyType 
} 
trait B extends Abstract { 
    trait MyType { 
    def bMethod : Int 
    } 
} 
trait A extends B { 
    trait MyType extends super.MyType { 
    } 
} 

Peki umarım bu sizin aradığınız şeydir.

+0

Ben senin yorumunu sanmıyorum çünkü "A # MyType" soyut, "BInner A # MyType ile" tuhaftır "doğrudur. Örnegin, '#InTemner with Abstract # MyType' yazmak da ayni derecede garip görünüyor. Spesifikasyonlar nerede olduğu hakkında oldukça açık bir ifadedir, fakat ne demek istedikleri hakkında bilgi vermezler (yani, bu kurallara ilişkin kuralların neden sağlam olması gerektiği). Tek ipucu, "bir türün sıfır olmayan değerlere sahip olma olasılığını yaklaşık olarak tahmin etmeleridir" ve argümanınızın bunu yakaladığını düşünmüyorum. – mergeconflict

+0

@mergeconflict Argümanı yeniden işledim ve şimdi daha açık olmasını umuyorum. – Leo