2011-08-27 40 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

除了使用类型成员之外,我不知道任何编码递归的实用方法。泛型和类型成员实际上并不完全等价,这是区分它们的一种情况。 –

相关问题