从一些简单的代码开始:
trait Moveable[A] {
def move(a: A): A
}
trait Animal {
def kick[A <: Animal: Moveable](a: A): A = implicitly[Moveable[A]] move a
}
object Cat {
implicit object CatMoveable extends Moveable[Cat] {
def move(cat: Cat): Cat = cat copy (pos = cat.pos + 4)
}
}
case class Cat(pos: Int) extends Animal
case class Dog(pos: Int) extends Animal
val someAnimal: Animal = Dog(0)
val kickedCat: Cat = someAnimal kick Cat(0)
println(kickedCat) // Cat(4)
我决定区分,让我们说Quadruped
和Biped
动物:
trait FourFeetMoveable[A] {
def moveWithFourFeets(a: A): A
}
trait TwoFeetMoveable[A] {
def moveWithTwoFeets(a: A): A
}
trait Animal {
def kick[A <: Animal /*: ??? */](a: A): A
}
trait Quadruped extends Animal {
def kick[A <: Animal: FourFeetMoveable](a: A): A = implicitly[FourFeetMoveable[A]] moveWithFourFeets a
}
trait Biped extends Animal {
def kick[A <: Animal: TwoFeetMoveable](a: A): A = implicitly[TwoFeetMoveable[A]] moveWithTwoFeets a
}
object Chicken {
implicit object ChickenTwoFeetMoveable extends TwoFeetMoveable[Chicken] {
def moveWithTwoFeets(chicken: Chicken): Chicken = chicken copy (pos = chicken.pos + 2)
}
}
case class Dog(pos: Int) extends Quadruped
case class Chicken(pos: Int) extends Biped
val someAnimal: Animal = Dog(0)
val kickedChicken: Chicken = someAnimal kick Chicken(0)
println(kickedChicken) // Chicken(2)
必须要有两个完全不同的类型FourFeetMoveable
和TwoFeetMoveable
,所以我不能用这样的东西来抽象它们:
trait Moveable[A] {
def move(a: A): A
}
那么如何在特征kick
中的方法Animal
上用作上下文绑定的类型类来抽象(参见???
)?
编辑
对不起,我应该让我的榜样更加清晰。让我们说被踢的效果可能是一些动作或其他一些动作。我想用类型类来抽象这个效果。
在下面的代码中,我展示了我的意思,并使用抽象类型成员KickingEffect
来抽象所需的类型类,如0__所提议的那样:
trait StumbleEffect[A <: Animal] {
def stumble(a: A): A
}
trait GlideEffect[A <: Animal] {
def glide(a: A): A
}
trait Animal {
type KickingEffect[A <: Animal]
def kick[A <: Animal: KickingEffect](a: A): A
}
trait Biped extends Animal {
type KickingEffect[A <: Animal] = StumbleEffect[A]
override def kick[A <: Animal: StumbleEffect](a: A): A = implicitly[StumbleEffect[A]] stumble a
}
trait Quadruped extends Animal {
type KickingEffect[A <: Animal] = GlideEffect[A]
override def kick[A <: Animal: GlideEffect](a: A): A = implicitly[GlideEffect[A]] glide a
}
object Dog {
implicit object DogGlideEffect extends GlideEffect[Dog] {
def glide(dog: Dog): Dog = dog copy (pos = dog.pos + 4)
}
}
case class Dog(pos: Int) extends Quadruped
case class Cat(pos: Int) extends Quadruped
case class Chicken(pos: Int) extends Biped
但是当涉及动物序列时,我遇到了另一个问题:
type Beast[A <: Animal, KE[_ <: Animal]] = A { type KickingEffect[X <: Animal] = KE[X] }
val dogBeast: Beast[Dog, GlideEffect] = Dog(0) // fine
type GlideBeasts[A <: Quadruped] = Beast[A, GlideEffect]
val glideBeasts: Seq[GlideBeasts[Quadruped]] = Seq(Dog(0), Cat(0)) // fine
def kickAll[A <: Animal, KE[_ <: Animal], KA <: Animal](kicker: Beast[A, KE])(animals: Seq[KA])(implicit ev: kicker.KickingEffect[KA]): Seq[KA] = {
for (a <- animals) yield kicker kick a
}
val cat = Cat(0)
val dog = Dog(0)
kickAll(cat)(Seq(dog)) // wrong inferred kinds of type arguments
kickAll[Cat, GlideEffect, Dog](cat)(Seq(dog)) // missing implicit evidence
答案 0 :(得分:2)
喜欢这个吗?
trait Moveable[A] {
def move(a: A): A
}
trait FourFeetMoveable[A] extends Moveable[A]
trait TwoFeetMoveable[A] extends Moveable[A]
trait Animal {
type CanKick[A] <: Moveable[A]
def kick[A <: Animal : CanKick](a: A): A = implicitly[CanKick[A]] move a
}
trait Quadruped extends Animal {
type CanKick[A] = FourFeetMoveable[A]
}
trait Biped extends Animal {
type CanKick[A] = TwoFeetMoveable[A]
}
关于编辑:我建议此时不要进一步尝试使用类型进行建模,除非它在您的应用程序或纯思想实验中确实是一个非常关键的点。您可以通过类型安全设计轻松过度设计,然后设计工作量和应用价值之间的比例变得越来越大;我只是放弃一些编译时的安全性,并进行模式匹配和运行时错误。
如果您 想要遵循类型路线,只要您有集合,就需要HLists之类的东西来保留集合成员的各个类型。
无论如何,您可以使您的示例工作(使用显式类型参数):
def kickAll[A <: Animal, KE[_ <: Animal], KA <: Animal](
kicker: Beast[A, KE])(animals: Seq[KA])(implicit effect: KE[KA]): Seq[KA] = {
for (a <- animals) yield kicker kick a
}
val cat = Cat(0)
val dog = Dog(0)
kickAll(cat)(Seq(dog)) // still doesn't figure out the types
kickAll[Cat, GlideEffect, Dog](cat)(Seq(dog)) // ok!
如上所述,当您尝试使用异构列表(例如,需要不同的效果)时,会出现棘手或不可能的部分。对于序列的元素,您可能会使用辅助类型类,因此可以在每个项目之前解析implicits。
答案 1 :(得分:1)
作为旁注,我发现不引入类型界限总是有用的,直到真正需要它们为止。您不仅可以安全地进行大量输入(没有双关语),而且还可以保持选项打开(例如,以后的方差注释)。以下是完全足够的:
trait StumbleEffect[A] {
def stumble(a: A): A
}
trait GlideEffect[A] {
def glide(a: A): A
}
trait Animal {
type KickingEffect[A]
def kick[A : KickingEffect](a: A): A
}
trait Biped extends Animal {
type KickingEffect[A] = StumbleEffect[A]
override def kick[A : StumbleEffect](a: A): A =
implicitly[StumbleEffect[A]] stumble a
}
trait Quadruped extends Animal {
type KickingEffect[A] = GlideEffect[A]
override def kick[A : GlideEffect](a: A): A = implicitly[GlideEffect[A]] glide a
}
等