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
'sforSome
restriction +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,
Cat
both is aFeline
and is aPetCareInfo
. Here's an alternative that makesPetCareInfo
a member ofPet
, so that aCat
has aPetCareInfo
. (I'm assuming that this makes sense. You could equally have aPet
member ofPetCareInfo
if that's more appropriate.)This latter approach could also be used to hide
PetCareInfo
(if theinfo
member 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
'sT
must be derived fromPet[T]
; however, you have only defined aT
forPetCareInfo
to be derived fromPetCareInfo[T]
, and aPetCareInfo[T]
has no expressed relationship to aPet[T]
. Theself
declaration simply constrains the type of any concretePetCareInfo
instance, and cannot be used to change the definition of what aT
represents.That is,
T
must be derived fromPetCareInfo[T]
andself
must belong to an object that extendsT with a Pet[T]
. However, sinceT
is 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.