Scala:限制特征混入 f 有界多态特征

Scala: Restricting traits mixing-in f-bounded polymorphic traits

我有:

trait Pet[T <: Pet[T]] {    //disallows: Dog extends Pet[String]
  self: T =>                //disallows: Dog extends Pet[Monkey]
  def rename(s: String): T
  def name: String
}

现在可以像 Feline 这样扩展 Pet class 的特征可以很容易地添加如下:

trait Feline[T <: Feline[T]] extends Pet[T] {
  self: T =>
  def pur : Unit = println("purrrr")
  def scratch: Unit
}

但是,如果我要在 Pet 中引入一个类型与自身类型的混合,例如:

trait PetCareInfo[T <: PetCareInfo[T]] {
    self: T with Pet[T] =>
    def registerPet: Unit
  }

我收到错误:

type arguments [T] do not conform to trait Pet's type parameter bounds [T <: Pet[T]]

我的理解是,这是因为 PetCareInfo 中的自类型检查会单独查看类型 A with B,因此未通过限制。 (不确定这是错误还是功能)

我可以改用存在类型:

type TypeRestriction: Pet[A] forSome {type A}

trait PetCareInfo[T <: PetCareInfo[T]] {
    self: T with TypeRestriction => //mix-in restriction
    def registerPet: Unit
  }

这还挺管用的。两个问题:

  1. 我无法在混入限制行直接定义存在类型。我得到:

; expected but 'forSome' found.

有办法解决这个问题吗?

  1. 实际上PetCareInfoforSome限制+Pet自己的限制意味着我不能有:

    class Cat extends Pet[Dog] with PetCareInfo[Cat]

但是我想知道有没有办法不依赖于Pet

更新

对于问题 2,我可以将现有的类型限制更改为:

type Restriction[T] = A with Pet[A] forSome {type A <: PetCareInfo[T]}

trait PetCareInfo[T <: PetCareInfo[T]] {
  self: Restriction[T] =>
  def registerPet: Unit
}

这似乎解决了问题。虽然,仍然不能保证结构 A 类型与 T 相同,所以我们仍然依赖于 Pet。 :(

试试这个:

trait PetCareInfo[T <: Pet[T] with PetCareInfo[T]] {
  self: T =>
  def registerPet: Unit
}

abstract class Cat extends Feline[Cat] with PetCareInfo[Cat] // OK
abstract class Dog extends Pet[Dog] with PetCareInfo[Dog] // OK
abstract class Tiger extends Feline[Tiger] with PetCareInfo[Cat] // Error.

Update:上面演示了一个is a的关系。也就是说,Cat都是FelinePetCareInfo。这是使 PetCareInfo 成为 Pet 成员的替代方法,因此 Cat 具有 PetCareInfo。 (我假设这是有道理的。如果更合适的话,您同样可以拥有 PetCareInfoPet 成员。)

// Change of emphasis: T is type of Pet. OK since we're in charge of its definition.
trait PetCareInfo[T <: Pet[T]] {
  // Etc.
}

trait Pet[T <: Pet[T]] {
  // Etc.
  val info: PetCareInfo[T]
}

abstract class Dog extends Pet[Dog] {
  // Etc.
  override val info = new PetCareInfo[Dog] {
     // Define a what a PetCareInfo looks like for a dog.
  }
}

后一种方法也可用于隐藏 PetCareInfo(如果 info 成员是 private),以防此类详细信息对代码用户无用。

更新 2:顺便说一句,关于以下错误“type arguments [T] do not conform to trait Pet's type parameter bounds [T <: Pet[T]]”:

trait PetCareInfo[T <: PetCareInfo[T]] {
  self: T with Pet[T] => // <- Error
  def registerPet: Unit
}

消息不言自明:PetT 必须派生自 Pet[T];然而,您只为 PetCareInfo 定义了一个 T 派生自 PetCareInfo[T],而 PetCareInfo[T]Pet[T] 没有明确的关系。 self 声明仅限制任何具体 PetCareInfo 实例的类型,不能用于更改 T 表示的定义。

也就是说,T 必须派生自 PetCareInfo[T],而 self 必须属于扩展 T with a Pet[T] 的对象。但是,由于 T 不是从 Pet[T] 派生的,因此不可能创建这样的实例,因此会出现错误。所以,这不是错误,而是必要的类型检查。