用一些簡單的代碼開始:摘要在類型類
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)
我決定來區分,let's說,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)
A必須是擁有兩個完全不同的typeclasses FourFeetMoveable
和TwoFeetMoveable
,所以我can't抽象他們這樣的事情:
trait Moveable[A] {
def move(a: A): A
}
那麼我如何抽象出特性Animal
(請參閱???
)中用作上下文綁定方法kick
的類型?
編輯
對不起,我應該讓我的例子更清晰。可以說,被踢的效果可能是一些動作或其他動作。我想用類型類抽象出這個效果。 在下面的代碼,我展示了我的意思,並且還使用了抽象類型成員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
這在我看來,兩足動物和四足動物之間的差別並不需要有兩個類型的類。如果兩者具有相同的界面,爲什麼不想只使用'可移動[雞]'和'可移動[狗]'?事實上,如果你有兩個類型的類,沒有編譯器檢查會阻止你意外地創建一個'TwoFeetMoveable [Dog]'。 – dyross
@dyross抱歉,我最初的例子是誤導性的,請參閱編輯。 –