2012-09-24 65 views
1

用一些簡單的代碼開始:摘要在類型類

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說,QuadrupedBiped動物:

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 FourFeetMoveableTwoFeetMoveable,所以我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 
+0

這在我看來,兩足動物和四足動物之間的差別並不需要有兩個類型的類。如果兩者具有相同的界面,爲什麼不想只使用'可移動[雞]'和'可移動[狗]'?事實上,如果你有兩個類型的類,沒有編譯器檢查會阻止你意外地創建一個'TwoFeetMoveable [Dog]'。 – dyross

+0

@dyross抱歉,我最初的例子是誤導性的,請參閱編輯。 –

回答

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。

+0

不錯的主意!在我的編輯中使用了抽象類型成員! –

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 
}