Scala: ограничивающие черты, смешивающие f-ограниченные полиморфные черты

Я имею:

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 класс можно легко добавить следующим образом:

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
  }

Я получаю ошибку:

аргументы типа [T] не соответствуют границам параметров типа черты Pet [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. Я не могу напрямую определить экзистенциальный тип в строке ограничения ввода. Я получил:

; ожидаемый, но 'forSome' найден.

Есть ли способ обойти это?

  1. На практике PetCareInfo"s forSome ограничение + 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,:(

1 ответ

Решение

Попробуй это:

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.

Обновление: выше демонстрирует отношения. То есть, Cat оба это Feline и является PetCareInfo, Вот альтернатива, которая делает PetCareInfo член Pet так, чтобы Cat имеет PetCareInfo, (Я предполагаю, что это имеет смысл. Вы могли бы иметь Pet член PetCareInfo если это более уместно.)

// 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
}

сообщение говорит само за себя: Pet "s T должен быть получен из Pet[T]; однако вы только определили T за PetCareInfo быть полученным из PetCareInfo[T] и PetCareInfo[T] не имеет выраженного отношения к Pet[T], self Объявление просто ограничивает тип любого конкретного PetCareInfo экземпляр, и не может быть использован для изменения определения того, что T представляет собой.

То есть, T должен быть получен из PetCareInfo[T] а также self должен принадлежать объекту, который расширяется T with a Pet[T], Тем не менее, так как T не является производным от Pet[T], невозможно создать такой экземпляр, следовательно, ошибка. Так что это не ошибка, а необходимая проверка типов.

Другие вопросы по тегам