Peter Schmitz
Peter Schmitz

Reputation: 5844

MyType Type Mismatch

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.

Upvotes: 1

Views: 240

Answers (3)

Peter Schmitz
Peter Schmitz

Reputation: 5844

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:

trait Excitable[T] { // TYPECLASS
  def withMoreAnger(t: T): T
}
trait Animal {
  type SpittableAnimal <: Animal
  def spitAt[A <: SpittableAnimal: Excitable](a: A): A
}
trait ExcitableAnimal extends Animal {
  type SpittableAnimal = ExcitableAnimal
  def spitAt[A <: SpittableAnimal: Excitable](a: A): A = implicitly[Excitable[A]] withMoreAnger a
}

object Dog {
  implicit object ExcitableDog extends Excitable[Dog] {
    def withMoreAnger(dog: Dog): Dog = dog copy (anger = dog.anger + 1)
  }
}
case class Dog(anger: Int) extends Quadruped

object Cat {
  implicit object ExcitableCat extends Excitable[Cat] {
    def withMoreAnger(cat: Cat): Cat = cat copy (anger = cat.anger + 1)
  }
}
case class Cat(anger: Int) extends Quadruped

sealed trait Quadruped extends ExcitableAnimal // sealed: to couple pattern match at implicit object ExcitableQuadruped and all subclasses of Quadruped
object Quadruped {
  implicit object ExcitableQuadruped extends Excitable[Quadruped] {
    def withMoreAnger(quadruped: Quadruped): Quadruped = {
      quadruped match {
        case dog: Dog => implicitly[Excitable[Dog]].withMoreAnger(dog)
        case cat: Cat => implicitly[Excitable[Cat]].withMoreAnger(cat)
      }
    }
  }
}

val dog = Dog(anger = 0)
val cat = Cat(anger = 0)
val angryCat: Cat = dog spitAt cat // fine
val anotherDog = Dog(0)
val animals: Seq[Quadruped] = Seq(dog, cat)
val angryAnimals: Seq[Quadruped] = for (a <- animals) yield anotherDog spitAt a // fine
val podAnimals: Seq[Quadruped] = for (a <- angryAnimals) yield anotherDog spitAt a // fine, still a Seq[Quadruped]

Upvotes: 0

som-snytt
som-snytt

Reputation: 39587

Here's another take. I guess this would be the "spit take."

It just shows variance on the type param, and a revised signature for spitAt.

It also shows an example, in Worm, of that other ageless question, what good is letting you create a concrete class with an abstract type member?

package spit

import language.higherKinds

trait Excitable[+SELF] { self: SELF =>
  def withMoreAnger: SELF
}
trait Animal[+SELF] { self: SELF =>
  type SpatAt = SELF // to reveal SELF for method spitAt used as a dependent method type
  type SpittableAnimal[S] <: Animal[S]
  def spitAt[A](a: SpittableAnimal[A]): a.SpatAt
}
trait ExcitableAnimal[+SELF] extends Animal[SELF] with Excitable[SELF] { self: SELF =>
  type SpittableAnimal[S] = ExcitableAnimal[S]
  def spitAt[A](a: SpittableAnimal[A]): a.SpatAt = 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)
}
trait Vermiform[SELF] extends Animal[SELF] { self: SELF =>
  // worm saliva is actually quite pleasant
  def spitAt[A](a: SpittableAnimal[A]): a.SpatAt = a.asInstanceOf[A]
}
case class Worm() extends Vermiform[Worm]

object Test {
  def main(args: Array[String]) {
    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 = for (a <- animals) yield anotherDog spitAt a
    val podAnimals = for (a <- angryAnimals) yield anotherDog spitAt a
    println(animals)
    println(angryAnimals)
    println(podAnimals)

    val worm = Worm()
    //println(worm spitAt dog) // Worms don't spit
  }
}

Upvotes: 1

som-snytt
som-snytt

Reputation: 39587

Do you enjoy zee pain? Do you! :)

I can't belief this question enjoys no love.

$ smala spit.Test
List(mild puppy, sweet kitteh)
List(angry puppy, gnarly kitteh)
List(angry hound, gnarly pussy)

Who can resist the gnarly kitteh?

Upvote me or the kitteh become angry! Very angry!

Among the questions about MyType, we find folks saying just use typeclasses. Just do it.

Easy to grant who may spit and be spat upon, and easy to read the code, perhaps.

I meant to get clever, for instance, a really angry cat turns into a GhostCat (anger > 9 lives), but I must go chauffeur at kindergarten...

package spit

import language.{ higherKinds, implicitConversions }

trait Excitable[SELF] { self: SELF =>
  def withMoreAnger: SELF
}
trait Animal[SELF] { self: SELF =>
  type SpatAt = SELF
  type SpittableAnimal[S] <: Animal[S]
}
trait ExcitableAnimal[SELF] extends Animal[SELF] with Excitable[SELF] { self: SELF =>
}
object ExcitableAnimal{
  implicit def toSpitter[S](a: ExcitableAnimal[S]) = new Spitter(a)
  implicit def toSpittee[S](a: ExcitableAnimal[S]) = new Spittee(a)
}
trait Quadruped[SELF] extends ExcitableAnimal[SELF] { self: SELF =>
}
class Dog(anger: Int) extends Quadruped[Dog] {
  def withMoreAnger: Dog = new Dog(anger + 2)
  override def toString = s"${if (anger > 0) "angry" else "mild"} ${if (anger > 2) "hound" else "puppy"}"
}
class Cat(anger: Int) extends Quadruped[Cat] {
  def withMoreAnger: Cat = new Cat(anger + 1)
  override def toString = s"${if (anger > 0) "gnarly" else "sweet"} ${if (anger > 1) "pussy" else "kitteh"}"
}

class Spitter[S](val spitter: Animal[S]) extends AnyVal {
  def spitAt[T](spittee: ExcitableAnimal[T]) = spittee.spatUpon
}
class Spittee[S](val spittee: ExcitableAnimal[S]) extends AnyVal {
  def spatUpon = spittee.withMoreAnger
}
object Test {
  def Dog(anger: Int) = new Dog(anger)
  def Cat(anger: Int) = new Cat(anger)
  def main(args: Array[String]) {
    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)
    println(animals)
    val angryAnimals = for (a <- animals) yield anotherDog spitAt a
    println(angryAnimals)
    val poAnimals = for (a <- angryAnimals) yield anotherDog spitAt a
    println(poAnimals)
  }
}

For reference, another character:

trait Vermiform[SELF] extends Animal[SELF] { self: SELF =>
  // worm saliva is actually quite pleasant
  def spitAt[A <: SpittableAnimal[A]](a: A): a.SpatAt = a
} // not excitable
case class Worm() extends Vermiform[Worm]

In the car just now, I found myself wondering if worm saliva does in fact have a sedative effect.

Upvotes: 1

Related Questions