Scala 2.10.4 を使用しています。
類推をむき出しにしてください - 実際のコードは複雑なプログラムに深く埋め込まれているので、それを説明するのではなく、時間をかけて動物について話すために問題を抽象化します ;-)
scala には 2 つの特徴があります。たとえば、次のとおりです。
Animal、およびHouseBase。
Animal を変更することはできませんが、Dog、Rabbit、Fish などのクラスを継承しています。面倒なことに、使用するすべてのサブクラスを所有しているわけではないため、すべてのサブクラスを変更することはできません。
私の動物はすべてどこかに住んでいます。彼らの家は HouseBase から継承する必要があります。HouseBase とそのサブクラスを変更できます (必要に応じて、別の抽象化レイヤーを介して)。
したがって、Dog は Animal のサブクラスであり、HouseBase のサブクラスである Kennel に住んでいます。
うさぎは小屋に、魚は水槽に住む。
ここでは 1 対 1 の関係が強制されていないことに注意してください。魚は池にも住むことができ、それも処理できる必要があります。
私が望んでいたのは、抽象型 Animal を介して参照される具体的な動物 (Fish など) が与えられ、具体的な戻り値の型 (Tank など) が与えられた場合、Scala は正しい暗黙のパラメーターを自動的に選択できるということでした。私が下に持っているデザイン。
object AnimalSelectionProblem extends App {
def abstractFish : Animal = new Fish(true, 20.0)
def concreteFish : Fish = new Fish(false, 30.0)
def abstractDog : Animal = new Dog("tasty bone")
def concreteDog : Dog = new Dog("yummy bone")
def abstractRabbit : Animal = new Rabbit(5)
def concreteRabbit : Rabbit = new Rabbit(10)
import HouseImplicits._
val myTank1: Tank = HouseImplicits.create(abstractFish)
val myTank2: Tank = HouseImplicits.create(concreteFish)
val myKennel1: Kennel = HouseImplicits.create(abstractDog)
val myKennel2: Kennel = HouseImplicits.create(concreteDog) // This works
val myhutch1: Hutch = HouseImplicits.create(abstractRabbit)
val myhutch2: Hutch = HouseImplicits.create(concreteRabbit) // This works
}
ただし、2 つの関連する問題があります。
問題 1 - 動物が抽象として参照されている場合、暗黙的なパラメーターは、基になる具象型ではなく抽象型 (動物) を取る関数のみを検索します。Scala は実行時間情報を使用していないように見えるので、解決策は ClassTags を使用することではないでしょうか? 私はこれを実装しようとしましたが、どうしようもなく迷ってしまいました (私は Scala にかなり慣れていません!)。
問題 2 - 私の動物が複数の種類の家に住むことができる場合、同様の問題が発生します。具体的な戻り値の型が指定されていても、コンパイラは Fish の 2 つの暗黙的なオブジェクトがあいまいであると判断します。私はここで何をすべきかについて少し困惑しています!
実行時に型に一致するように手動のボイラープレートを使用してソリューションを考え出すことはできますが、これはあまり拡張可能ではありません。
どんなアイデアもありがたく受け取った!コードの残りの部分は以下のとおりです。
編集 - これらのリンクは、私が疑っていたことを裏付けているようです。そのコンパイル時のポリモーフィズムが使用されているため、実行時の型を知ることができません。
http://like-a-boss.net/2013/03/29/polymorphism-and-typeclasses-in-scala.html
それで、私の質問は、これを考えると、ランタイムディスパッチを使用するように私の例を変更する方法があると思いますか?
動物:
trait Animal {
}
class Dog(val boneName: String) extends Animal
class Rabbit(val length: Int) extends Animal
class Fish(val likesFrogs: Boolean, val optimumTemp: Double) extends Animal
ハウスとインプリシット:
sealed trait HouseBase
// Made up some arbitrary member variables
case class Kennel(posessions: Seq[String]) extends HouseBase
case class Hutch(length: Int) extends HouseBase
case class Tank(waterTemp: Double) extends HouseBase
case class Pond(containsFrog: Boolean) extends HouseBase
sealed trait HouseCreator[A <: Animal, HB <: HouseBase] {
def create(animal: A): HB
}
object HouseImplicits {
implicit object BuildKennelForDog extends HouseCreator[Dog, Kennel] {
override def create(dog: Dog): Kennel = {
new Kennel(Seq(dog.boneName))
}
}
implicit object BuildTankForFish extends HouseCreator[Fish, Tank] {
override def create(fish: Fish): Tank = {
new Tank(fish.optimumTemp)
}
}
implicit object BuildPondForFish extends HouseCreator[Fish, Pond] {
override def create(fish: Fish): Pond = {
new Pond(fish.likesFrogs)
}
}
implicit object BuildHutchForRabbit extends HouseCreator[Rabbit, Hutch] {
override def create(rabbit: Rabbit): Hutch = {
new Hutch(rabbit.length*5)
}
}
def create[A <: Animal, H <: HouseBase](animal: A)(implicit house: HouseCreator[A,H]) : H = {
val newHouse = house.create(animal)
newHouse
}
}