I have:
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
}
Now a trait like Feline that would extend the Pet class can be easily added as follows:
trait Feline[T <: Feline[T]] extends Pet[T] {
self: T =>
def pur : Unit = println("purrrr")
def scratch: Unit
}
But if I were to introduce a type mixing in Pet with a self-type such as:
trait PetCareInfo[T <: PetCareInfo[T]] {
self: T with Pet[T] =>
def registerPet: Unit
}
I get the error:
type arguments [T] do not conform to trait Pet's type parameter bounds [T <: Pet[T]]
my understanding is that this is because self-type check in PetCareInfo looks at the types A with B individually and as such fails the restriction. (not sure if this is a bug or feature)
I can use existential types instead:
type TypeRestriction: Pet[A] forSome {type A}
trait PetCareInfo[T <: PetCareInfo[T]] {
self: T with TypeRestriction => //mix-in restriction
def registerPet: Unit
}
and that would kinda work. Two questions:
- I cannot directly define the the existential type at the mix-in restriction line. I get:
; expected but 'forSome' found.
Is there a way of getting around this?
In practice the
PetCareInfo'sforSomerestriction +Pet's own restriction means that I can not have:class Cat extends Pet[Dog] with PetCareInfo[Cat]
But I would like to know if there is a way of not depending on Pet for this.
Update:
For question 2, I can change the existing type restriction to be:
type Restriction[T] = A with Pet[A] forSome {type A <: PetCareInfo[T]}
trait PetCareInfo[T <: PetCareInfo[T]] {
self: Restriction[T] =>
def registerPet: Unit
}
and that seems to be solving the problem. Although, there is still no guarantee that the structural A type will be the same as T, so we are still depending on Pet. :(
Try this:
Update: The above demonstrates an is a relationship. That is,
Catboth is aFelineand is aPetCareInfo. Here's an alternative that makesPetCareInfoa member ofPet, so that aCathas aPetCareInfo. (I'm assuming that this makes sense. You could equally have aPetmember ofPetCareInfoif that's more appropriate.)This latter approach could also be used to hide
PetCareInfo(if theinfomember wasprivate), in the event that such details are not useful to the code user.UPDATE 2: BTW, regarding the error "
type arguments [T] do not conform to trait Pet's type parameter bounds [T <: Pet[T]]" for:the message is self-explanatory:
Pet'sTmust be derived fromPet[T]; however, you have only defined aTforPetCareInfoto be derived fromPetCareInfo[T], and aPetCareInfo[T]has no expressed relationship to aPet[T]. Theselfdeclaration simply constrains the type of any concretePetCareInfoinstance, and cannot be used to change the definition of what aTrepresents.That is,
Tmust be derived fromPetCareInfo[T]andselfmust belong to an object that extendsT with a Pet[T]. However, sinceTis not derived fromPet[T], it's impossible to create such an instance, hence the error. So, it's not a bug but an essential type check.