3

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

https://softwareengineering.stackexchange.com/questions/258698/is-it-possible-to-have-ad-hoc-polymorphism-with-runtime-dispatch

それで、私の質問は、これを考えると、ランタイムディスパッチを使用するように私の例を変更する方法があると思いますか?

動物:

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
  }
}
4

2 に答える 2

2

したがって、基本的には次のデザインが必要です。

  • コンパイル時には、 の具体的な型HouseBaseがわかっています。
  • コンパイル時には、 の具体的な型Animal不明です。
  • 提供された実行時の動物データの特定のタイプHouseBaseを作成します。Animal
  • 実装を変更することはできず、実際には実装Animalを変更したくありませんHouseBase

もちろん、望ましいことは、Animalコンパイル時に s の具体的な型を利用できるようにすることです。それについてある程度の知識があるように見えるので (HouseBaseコンパイル時に動物変数に対して作成するものを知っている)、型安全なキャストを使用して具象型のshapelessを取得しようとする場合があります。OptionAnimal

しかし、それが不可能な場合は、動物のランタイム ディスパッチを使用する必要があります。

createその場合、メソッドには次の署名が必要だと思います。

def create[HB <: HouseBase](animal: Animal): Option[HB]

の具体的な型を知っているHouseBaseので、それを型パラメーターとして渡すこともできます。戻り値はOption、提供された動物の型と具体的な動物の型との間の不一致の可能性を考慮したものです。HouseBase

HouseBaseこれを実装する 1 つの可能な方法は、 s からの sの生成に関するすべての知識を持つ単一のオブジェクトを使用した次のコードです(作成コードを具体的なs のAnimalコンパニオン オブジェクトに移動することによっても同じことを達成できるはずです)。HouseBase

sealed trait HouseCreator[HB <: HouseBase] {
  def create(animal: Animal): Option[HB]
}

object HouseCreator {
  implicit object KennelCreator extends HouseCreator[Kennel] {
    def create(animal: Animal): Option[Kennel] = animal match {
      case dog: Dog => Some(Kennel(Seq(dog.boneName)))
      case _ => None
    }
  }

  implicit object HutchCreator extends HouseCreator[Hutch] {
    def create(animal: Animal): Option[Hutch] = animal match {
      case rabbit: Rabbit => Some(Hutch(rabbit.length * 5))
      case _ => None
    }
  }

  implicit object TankCreator extends HouseCreator[Tank] {
    def create(animal: Animal): Option[Tank] = animal match {
      case fish: Fish => Some(Tank(fish.optimumTemp))
      case _ => None
    }
  }

  implicit object PondCreator extends HouseCreator[Pond] {
    def create(animal: Animal): Option[Pond] = animal match {
      case fish: Fish => Some(Pond(fish.likesFrogs))
      case _ => None
    }
  }

  def create[HB <: HouseBase : HouseCreator](animal: Animal): Option[HB] =
    implicitly[HouseCreator[HB]].create(animal)
}

次に、次の方法で関数を呼び出すことができます。

val myTank1: Option[Tank] = HouseCreator.create[Tank](abstractFish)
val myTank2: Option[Tank] = HouseCreator.create[Tank](concreteFish)

// Types of the variables can also be inferred automatically
val myKennel1 = HouseCreator.create[Kennel](abstractDog)
val myKennel2 = HouseCreator.create[Kennel](concreteDog)

val myhutch1 = HouseCreator.create[Hutch](abstractRabbit)
val myhutch2 = HouseCreator.create[Hutch](concreteRabbit)

また、定型コードはsHouseCreatorを使用して削減できます。PartialFunction

sealed trait HouseCreator[HB <: HouseBase] {
  def create: PartialFunction[Animal, HB]
}

object HouseCreator {
  implicit object KennelCreator extends HouseCreator[Kennel] {
    def create = {
      case dog: Dog => Kennel(Seq(dog.boneName))
    }
  }

  implicit object HutchCreator extends HouseCreator[Hutch] {
    def create = {
      case rabbit: Rabbit => Hutch(rabbit.length * 5)
    }
  }

  implicit object TankCreator extends HouseCreator[Tank] {
    def create = {
      case fish: Fish => Tank(fish.optimumTemp)
    }
  }

  implicit object PondCreator extends HouseCreator[Pond] {
    def create = {
      case fish: Fish => Pond(fish.likesFrogs)
    }
  }

  def create[HB <: HouseBase : HouseCreator](animal: Animal): Option[HB] =
    implicitly[HouseCreator[HB]].create.lift(animal)
}
于 2015-04-12T18:59:29.273 に答える