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)

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)

FourFeetMoveable2 つのまったく異なる型クラスとを持たなければならないTwoFeetMoveableので、次のようなものでそれらを抽象化することはできません。

trait Moveable[A] {
  def move(a: A): A
}

kickでは、トレイトのメソッドでコンテキスト バインドとして使用される型クラスを抽象化するにはどうすればよいでしょうかAnimal( を参照???)。

編集

申し訳ありませんが、例をより明確にする必要がありました。蹴られた効果が何らかの動きやその他のアクションになるとしましょう。その効果を型クラスで抽象化したかったのです。次のコードでは、私が何を意味するかを示しており、 0__が提案しKickingEffectたように、必要な型クラスを抽象化するために抽象型メンバーも使用しています。

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
4

2 に答える 2

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!

前述のように、異種のリストでこれを実行しようとすると (たとえば、異なる効果が必要な場合)、トリッキーまたは不可能な部分が生じます。代わりに、シーケンスの要素にヘルパー型クラスを使用することで回避できるため、前に項目ごとに暗黙を解決できます。

于 2012-09-24T18:40:12.890 に答える
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
}

于 2012-09-25T09:49:16.330 に答える