我从Landei here借用了MyType技巧。但最近我遇到了自我类型的问题。一个例子说明了我的意思:
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 !!!
据我所知,问题似乎是spitAt
方法中的下划线最终会为Any
产生a.SELF
。但是我怎样才能使这段代码有效呢?
我也试过这个:
def spitAt[A <: SpittableAnimal[A]](a: A): A = a.withMoreAnger
但是推断的类型参数不符合方法spitAt的类型参数边界,这对我来说很清楚,因为SELF
中元素的animals
类型参数至少受{{1}的限制。 }}不符合上面_ >: Cat with Dog <: Quadruped[_]
中的a.SELF
,A
,甚至spitAt
中A
的{{1}}:
spitAt
那么使def spitAt[A <: SpittableAnimal[S], S <: A](a: A): A = a.withMoreAnger
- 循环线有效的spitAt
方法的正确签名是什么?
也许SELF类型参数的方差注释(for
)可能会有所帮助,但我不知道如何。
答案 0 :(得分:1)
你喜欢zee疼痛吗?你呢! :)
我不相信这个问题没有爱。
$ smala spit.Test
List(mild puppy, sweet kitteh)
List(angry puppy, gnarly kitteh)
List(angry hound, gnarly pussy)
谁能抵抗这个粗糙的人?
Upvote me或kitteh生气!非常生气!
在关于MyType的问题中,我们发现人们只是说使用类型类。就这么做。
很容易授予那些可能会吐痰并吐口水,也许容易阅读代码的人。
我的意思是聪明,例如,一只非常生气的猫变成了GhostCat(愤怒&gt; 9人的生命),但我必须在幼儿园去司机......
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)
}
}
供参考,另一个角色:
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]
刚才在车上,我发现自己想知道蠕虫唾液是否确实具有镇静作用。
答案 1 :(得分:1)
这是另一种看法。我想这就是“吐痰”。
它只显示类型参数的差异,以及spitAt的修订签名。
它还在Worm中展示了另一个无代表问题的例子,让你创建一个具有抽象类型成员的具体类有什么好处?
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
}
}
答案 2 :(得分:0)
与此同时,我读到并记住了这一句:The Typeclass Pattern - An Alternative to Inheritance
正如user1296806提到here,类型类值得一试。所以这就是:
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]