2011-08-27 32 views
6

如果我有以下斯卡拉類型層次:Scala類型參數在作爲基類的參數使用時可以引用它自己嗎?

// Base traits 
trait TA[X <: TA[X,Y], Y <: TB[X,Y]] 
trait TB[X <: TA[X,Y], Y <: TB[X,Y]] 
trait TC[X <: TA[X,_]] 

// More specific traits 
trait TI[X <: TI[X,Y], Y <: TJ[X,Y]] extends TA[X,Y] 
trait TJ[X <: TI[X,Y], Y <: TJ[X,Y]] extends TB[X,Y] 
trait TK[X <: TI[X,_]] extends TC[X] 

// Concrete class that should implement TK, but does not compile 
class Z extends TK[TI[_,_]] 

// What is needed to be able to get class Z to compile 
// The reference of X to itself has been removed. 
trait TC2[X <: TA[_,_]] 
trait TK2[X <: TI[_,_]] extends TC2[X] 
class Z2 extends TK2[TI[_,_]] 

TC將是某種TA的一個通用管理。

傳統知識將是更具體的TA(TI)的更具體的經理。

Z將是管理實現TI的任何對象的具體實現。

Z不合法,但Z2是。不幸的是,TC和TK比TC2和TK2更具體。那麼有沒有一種方法可以用TC和TK來代替TC2和TK2來聲明Z?

[編輯]我沒有在我原來的問題中說,我有點理解爲什麼Z不健全。我真正想知道的是,如果有這樣一種說法:

class Z3 extends TK[TI[TI,_]] 

回答

7

當您有複雜的相互遞歸類型邊界時,通常會很有幫助,看看您是否可以使用抽象類型成員將您的問題轉換爲等價的問題。如果我們這樣做機械的,你的基地,更具體的性狀最終看起來像,

// Base traits 
trait TA { 
    type X <: TA 
    type Y <: TB 
} 
trait TB { 
    type X <: TA 
    type Y <: TB 
} 

trait TC { 
    self => 
    type X <: TA { type X <: self.X } 
} 

// More specific traits 
trait TI extends TA { 
    type X <: TI 
    type Y <: TJ 
} 

trait TJ extends TB { 
    type X <: TI 
    type Y <: TJ 
} 

trait TK { 
    self => 
    type X <: TI { type X <: self.X } 
} 

,現在我們必須將z作爲一個簡單的定義,

class Z extends TK { 
    type X = TI 
} 

注意,成員的定義TA,TB和TI,TJ基本相同。由於這些類型現在鍵入成員,我們可以將它們剔除到共同的基類,像這樣,

// Base traits 
trait T0 { 
    type X <: TA 
    type Y <: TB 
} 
trait TA extends T0 
trait TB extends T0 

trait TC { 
    self => 
    type X <: TA { type X <: self.X } 
} 

// More specific traits 
trait T1 extends T0 { 
    type X <: TI 
    type Y <: TJ 
} 

trait TI extends TA with T1 
trait TJ extends TB with T1 

trait TK extends TC { 
    self => 
    type X <: TI { type X <: self.X } 
} 

class Z extends TK { 
    type X = TI 
} 
+0

雖然我可以跟隨你的論點,但我認爲你已經通過改變Z的含義解決了問題.Z是一個傳統知識,因此在我的例子中是一個TC,但它不是,也不應該是TI。如果TC是一個「經理人」,那麼TA就是一個辦公室,而TB則是與經理人不在乎的辦公室相關的東西,TK是一個會計經理,TI是一個會計辦公室,那麼Z就是同時又是會計經理和會計部門。另外,我想指出,在我的真實代碼中,技術援助和結核病會有所不同,因爲他們會定義自己的行爲。 –

+0

公平評論:我調整了上面Z的定義來解決它。請注意,您可以添加您想要的任何其他定義,TA,TB,TI,TJ ......您只需要重複類型成員即可。 –

3

這將是不合適的。這是爲什麼,用一個簡單的例子。我們不需要兩個通用參數,我們不需要亞型TI,TJ和TK要麼

trait TA[X <: TA[X]] 
trait TC[X <: TA[X]] 
class Z extends TC[TA[_]] 

類型參數[TA [_]不符合特質TC的類型參數 界[X < :TA [X]]

讓我們來看看爲什麼這個聲明是不健全的,它是正確的,它失敗了。 讓我們添加一些代碼。我將TC更改爲class,以便代碼可以轉換爲java。 A trait也可以在scala中使用。

trait TA[X <: TA[X]] {def f(x: X) } 
class TC[X <: TA[X]] {def g(x: X) = x.f(x)} 

它工作正常,x: XTA[X],所以它有一個例行f,將接受一個X

如果我們試圖代替

class TC2[X <: TA[_]] {def g(x: X) = x.f(x)} 

那麼它失敗。我們知道在x上有一個f方法,但我們不知道需要什麼類型作爲參數,我們不知道x會沒事。事實上,假如我們定義

class T1 extends TA[T1]] {def f(t1: T1) = {}; def t1Only = println("only in T1")} 
class T2 extends TA[T1]] {def f(t1: T1) = t1.t1Only } 

現在,如果TC2是允許的,我可以創造一個TC2[T2],叫gT2,它會叫fT2T2。這是不允許的,正確的如T2沒有方法t1Only

這顯示了爲什麼TC不能接受TA[_]]作爲其參數,因爲這將允許T2,這與方法g不兼容。所以我們爲什麼不能用參數TA[_]來定義Z。這將是完全相同的Java,並與您的原始代碼以及。


編輯:我覺得有點內疚,我的答案。由於我給出了爲什麼不應該被允許的原因,我認爲會有一個簡單的解決方法。它失敗了,我沒有時間進一步調查,並張貼沒有提及它。解決方法是一種自我類型。如果我們這樣做

trait TA[X <: TA[X]] {self: X => } 

那麼我們就不能定義T2 extends TA[T1]。所以它原來的代碼更有限。但是我們必須接受原始代碼的限制,因爲它是不健全的。所以它不能只是一個語法技巧,它必須讓事情變得不可能,而不是。我認爲T2 extends TA[T1]可能不是有意的東西,而且這是防止的一件事。

顯然,它不是,同樣的錯誤。現在我沒有一個例子說明它不應該起作用。當然這並不意味着沒有。

然後,我看看邁爾的解決方案,想知道爲什麼T2 extends TA[T1]的可能性不會傷害它。如此反覆,丟棄TBYTITJTK

trait TA{type X} 
trait TC{self => type X <: TA{type X <: self.X} 
class Z extends TC{type X = TA} 

這編譯。而我們能做的

trait T1 extends TA{type X = T1} 
trait T2 extends TA{type X = T1} 

但是有一件事情我們不能做的:

trait TA {type X <: TA; def f(x: X)} 
trait TC {self => 
    type X <: TA{type X <: self.X} 
    def g(x: X) = x.f(x) 
} 

我們相處g下面的錯誤,對於x參數f

類型不匹配;實測值:x.type(與下面類型TC.this.X)所需 :x.x中

TC是不完全原酮(如最初,g被允許)。這是因爲TA上有type X <: self.X,而TC[X <: TA[X]]是更強的type X = self.X。如果我們寫出來,我們又回到原來的錯誤,Z不編譯。所以這個TC有點在原始的TCtype X = self.X)和TC2(不知道TA的X)之間。再次,對原始代碼的限制,我們無法定義g

如果限制是可以接受的,你沒事。我不知道如何將它寫成通用的(也不知道如何用抽象類型成員編寫自我類型{self : X =>)。邁爾斯絕對是專家,我敢肯定,他可以告訴他們做得如何,或者做不到這一點。

+0

萬里展示了它如何與抽象類型的工作,而你表現爲什麼沒有泛型工作。雖然這兩個答案都很有用,但它們都不告訴我,「Z3類擴展了傳統知識[TI [TI,_]]」是否實際上可以作爲泛型以及如何實現。由於泛型和抽象類型被認爲在Scala中是等價的,你會知道如何正確地使用泛型? –

+0

不幸的是,簡短的回答是我不知道。我不確定他們是100%相等的。我也不能像@Miles那樣轉換成類型成員。長時間的回答作爲編輯。 –

+0

除了使用類型成員之外,我不知道任何編碼遞歸的實用方法。泛型和類型成員實際上並不完全等價,這是區分它們的一種情況。 –

相關問題