2012-10-17 10 views
1

我有一個Accessor類檢索項目。它也可以將一個Item作爲參數,並從數據庫中返回該Item的最新版本。當它創建一個Item時,它將自身作爲參數傳遞給Item。如何使用scala的單例對象類型來要求通過構造函數的某個參數創建一個對象?

我希望編譯器靜態地要求Accessor實例只接受由它自己創建的項目。這是由How to use Scala's singleton-object types?涵蓋但是我也希望一個Item實例能夠將自己作爲參數傳遞給它自己的Accessor以檢索它自己的最新版本。

這樣做的困難是,在項目類定義的類型參數,像這樣

class Item[A <: Accessor](acc: A) 

不能引用ACC本身的類型。從項目acc.type <: A <: Accessor所以this項目Item[A]的角度,不是Item[acc.type]。因此,這並不工作:

class Item[A <: Accessor](acc: A) { 
    acc.accept(this) // Type Mismatch: found Item[A], required Item[Item.this.acc.type] 
} 

class Accessor { 
    def make() = new Item[this.type](this) 
    def accept(item: Item[this.type]) = "accepted" 
} 

然後我嘗試這樣做:

object A1 extends Accessor[A1.type](A1) // illegal cyclic reference involving object A1 

class Item[+A <: Accessor[A]](acc: A) { 
    acc.accept(this) 
    A1.accept(this) // Compile error (good) 
} 

class Accessor[+A <: Accessor[A]](me: => A) { 
    def make = new Item[A](me) 
    def accept(item: Item[A]) = "accepted" 
} 

哪裏的問題實際上是創建訪問器的一個實例。

我試圖上述這被證明是相同的基本困境的混亂化身的變化:

object A1 extends Accessor { 
    type A = A1.type 
    def me = A1 
} 

class Item[+AA <: Accessor](acc: AA {type A = AA}) { 
    acc.accept(this) 
    A1.accept(this) // Compile error (good) 
} 

class Accessor { 
    type A <: Accessor 
    def me: A // can't do {type A = A} because its a cyclic error again 
    def make = new Item[A](me) // Type Mismatch: found this.A, required this.A {type A = Accessor.this.A} 
    def accept(item: Item[A]) = "accepted" 
} 

最後我試圖使所述A類型參數逆變使得項目[A]是一種亞型項目[acc.type],並將被acc收錄。

val a1 = new Accessor 
val a2 = new Accessor 
val item1 = a1.make 
val item2 = a2.make 
val itemA = new Item[Accessor](a2) 
val item12 = new Item[A1.type](a2) // compile error (good) 

a1.accept(itemA) // no compile error (bad), but I can prevent creation of Item[Accessor]s 
a1.accept(item2) // compile error (good) 

class Item[-A <: Accessor](acc: A) { 
    acc.accept(this) 
    val acc2 = new Accessor 
    acc2.accept(this) // compile error (good) 
    // here Item[Accessor] <: Item[A] <: Item[acc.type] 
    // and Item[Accessor] <: Item[acc2.type] 
    // but Item[A] is not necessarily <: Item[acc2.type] 
} 

class Accessor { 
    def make() = new Item[this.type](this) 
    def accept(item: Item[this.type]) = "accepted" 
} 

這最接近我嘗試過的任何東西的工作。唯一的問題是,因爲我不能做到這一點充塞了我的對象層次:

class ImmutableAccessor extends Accessor 

class ImmutableItem[-A <: ImmutableAccessor](acc: A) extends Item[A] // fails due to contravariance in A 

如果只是有一些方法來指定一個類型參數必須是單式。因此,例如,你可以說(我在這裏發明了記號)

class Item[A:type <: Accessor](acc: A) 

而且A那麼將是acc的單類型,我們會笑着說。

回答

1

我們在這裏需要的祕密醬是利用Predef中的類<:<,使用隱式值。你是完全正確的,「與辛格爾頓」沒有做我們想要的一切。

class Item[-A <: Accessor](acc: A)(implicit sing: A <:< Singleton) { 
    acc.accept(this) 
} 

class Accessor { 
    def make() = new Item[this.type](this) 
    def accept(item: Item[this.type]) = "accepted" 
} 

class BetterItem[-B <: BetterAccessor](b: B)(implicit bsing: B <:< Singleton) 
    extends Item[B](b)(bsing) 
} 

class BetterAccessor extends Accessor 

現在繼承效果更好。我們可以做類似以下的東西:

val a1 = new Accessor 
val a2 = new Accessor 
val b3 = new BetterAccessor 

val i1 = a1.make 
val i3 = b3.make 

a1.accept(i1)  // GOOD 
//a2.accept(i1) // compile time error 
//b3.accept(i1) // compile time error 
//new Item[Accessor](a2) // compile time error 
//a1.accept(i3) // compile time error 
b3.accept(i3)  // GOOD 

而且我們在我們想要的地方得到編譯時錯誤。

0

我沒有關於處理繼承一個完整的解決方案,但我可以建議一個小的改動,以防止類似

a1.accept(itemA) 

線進行編譯。在Item的定義中包含一個明確的「with Singleton」。

class Item[-A <: Accessor with Singleton](acc: A) { 

然後嘗試創建一個新的「Item [Accessor]」不會編譯。

+0

「與單身」的東西看起來像我希望的功能。它應該允許像'class Item [A <:Accessor with Singleton](acc:A)'這樣的語句確保A與acc.type的類型完全相同,從而允許Item將自身傳遞給它的Accessor。不幸的是,編譯器似乎沒有認識到這個邏輯,仍然認爲A>:acc.type。 –

相關問題