I borrowed the MyType trick from Landei here. But recently I ran into a problem with the self type. An example shows what I mean:
trait Excitable[SELF] { self: SELF =>
def withMoreAnger: SELF
}
trait Animal[SELF0] { self: SELF0 =>
type SELF = SELF0 // to reveal SELF0 for method spitAt used as a dependent method type
type SpittableAnimal[S] <: Animal[S]
def spitAt[A <: SpittableAnimal[_]](a: A): a.SELF
}
trait ExcitableAnimal[SELF] extends Animal[SELF] with Excitable[SELF] { self: SELF =>
type SpittableAnimal[S] = ExcitableAnimal[S]
def spitAt[A <: SpittableAnimal[_]](a: A): a.SELF = a.withMoreAnger
}
trait Quadruped[SELF] extends ExcitableAnimal[SELF] { self: SELF => }
case class Dog(anger: Int) extends Quadruped[Dog] {
def withMoreAnger: Dog = copy(anger = anger + 1)
}
case class Cat(anger: Int) extends Quadruped[Cat] {
def withMoreAnger: Cat = copy(anger = anger + 1)
}
val dog = Dog(anger = 0)
val cat = Cat(anger = 0)
val angryCat: Cat = dog spitAt cat // fine
val anotherDog = Dog(0)
val animals = Seq(dog, cat)
val angryAnimals: Seq[Quadruped[_]] = for (a <- animals) yield anotherDog spitAt a // fine
val veryAngryAnimals: Seq[Quadruped[_]] = for (a <- angryAnimals) yield anotherDog spitAt a // type mismatch !!!
As far as I can reveal it, the problem seems to be that the underscore in the spitAt method yields an Any for a.SELF in the end. But how can I make this code work?
I also tried this:
def spitAt[A <: SpittableAnimal[A]](a: A): A = a.withMoreAnger
But then inferred type arguments do not conform to method spitAt’s type parameter bounds which is clear to me, since the SELF type argument of the elements in animals are at least bounded by _ >: Cat with Dog <: Quadruped[_] which do not conform with a.SELF, A in the spitAt above or even A in the spitAt below:
def spitAt[A <: SpittableAnimal[S], S <: A](a: A): A = a.withMoreAnger
So what is the right signature of the spitAt method to make the for-loop lines work?
Perhaps variance annotations (+SELF) of the SELF type parameter may be helpful, but I don´t know how.
Meanwhile I read on and remembered this one: The Typeclass Pattern – An Alternative to Inheritance
And as user1296806 mentioned here, typeclasses are worth a try. So here it is: