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
}
и это бы сработало. Два вопроса:
- Я не могу напрямую определить экзистенциальный тип в строке ограничения ввода. Я получил:
; ожидаемый, но 'forSome' найден.
Есть ли способ обойти это?
На практике
PetCareInfo
"sforSome
ограничение +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]
, невозможно создать такой экземпляр, следовательно, ошибка. Так что это не ошибка, а необходимая проверка типов.