2

これは、表現型に関する2つの 質問のフォローアップです。これらは、有界型メンバー(またはそのようなもの)の基礎となる型を表すように設計されたトレイトの型パラメーターです。有界型メンバーのConcreteGarageインスタンスを持つクラスのインスタンスを作成することに成功しました。carsCarType

trait Garage {
  type CarType <: Car[CarType]
  def cars: Seq[CarType]

  def copy(cars: Seq[CarType]): Garage

  def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
    cars.map {
      case `car` => car.refuel(fuel)
      case other => other
    })
}

class ConcreteGarage[C <: Car[C]](val cars: Seq[C]) extends Garage {
  type CarType = C
  def copy(cars: Seq[C]) = new ConcreteGarage(cars)
}

trait Car[C <: Car[C]] {
  type FuelType <: Fuel
  def fuel: FuelType

  def copy(fuel: C#FuelType): C

  def refuel(fuel: C#FuelType): C = copy(fuel)
}

class Ferrari(val fuel: Benzin) extends Car[Ferrari] {
  type FuelType = Benzin
  def copy(fuel: Benzin) = new Ferrari(fuel)
}

class Mustang(val fuel: Benzin) extends Car[Mustang] {
  type FuelType = Benzin
  def copy(fuel: Benzin) = new Mustang(fuel)
}

trait Fuel
case class Benzin() extends Fuel

単純である限り、sやsCarのようなsのインスタンスを簡単に作成し、それらをに入れることができます。FerrariMustangConcreteGarage

val newFerrari = new Ferrari(Benzin())
val newMustang = new Mustang(Benzin())

val ferrariGarage = new ConcreteGarage(Seq(newFerrari))
val mustangGarage = new ConcreteGarage(Seq(newMustang))

ただし、フラグに基づいてどちらか一方を返すだけで、その結果をガレージに入れようとすると、失敗します。

val likesFord = true
val new_car = if (likesFord) newFerrari else newMustang

val switchedGarage = new ConcreteGarage(Seq(new_car)) // Fails here

スイッチだけで正常にConcreteGarage機能します。コンストラクターの呼び出しが失敗し、かなり神秘的なエラーが発生します。

error: inferred type arguments [this.Car[_ >: this.Ferrari with this.Mustang <: this.Car[_ >: this.Ferrari with this.Mustang <: ScalaObject]{def fuel: this.Benzin; type FuelType<: this.Benzin}]{def fuel: this.Benzin; type FuelType<: this.Benzin}] do not conform to class ConcreteGarage's type parameter bounds [C <: this.Car[C]]
val switchedGarage = new ConcreteGarage(Seq(new_car)) // Fails here
                     ^

私はそれらの魔法の[C <: Car[C]]表現タイプのパラメータをどこにでも配置しようとしましたが、魔法の場所を見つけることに成功しませんでした。

4

1 に答える 1

2

エイリアスできる便利なスーパータイプはFerrariありません。Mustangこのアプローチで世界を裏返しにする必要があります。

Garage1つの可能性は、メソッドとして構造をに追加することCarです。

もう1つの可能性は、互換性のある車とガレージの作成を処理する「世界」を定義することです。

trait World {
   type CarType <: Car[CarType]
   def newCar() : CarType
   def newGarage(cars: Seq[CarType]) = new ConcreteGarage[CarType](cars)
}

class FerrariWorld extends World {
   type CarType = Ferrari
   def newCar() = new Ferrari(Benzin())
}

class FordWorld extends World {
   type CarType = Mustang
   def newCar() = new Mustang(Benzin())
}

def play(world: World) {
   val car = world.newCar()
   println(car)
   val gar = world.newGarage(Seq(car))
   println(gar)
}

def test(likesFord: Boolean) {
   val w = if(likesFord) new FordWorld else new FerrariWorld
   play(w)
}

test(true)
test(false)

これはかなり閉所恐怖症になる可能性があることがわかります。したがって、それは実際にはターゲットシナリオによって異なります。パス依存型は常に将来の制約を引き起こします。代わりに、タイプパラメータを使用したこのかなり単純なバリアントを検討してください。

trait Fuel { def liters: Int }
trait Make { def color: String }

case class Benzin(liters: Int = 0) extends Fuel
case class Diesel(liters: Int = 0) extends Fuel
case class Ferrari(color: String) extends Make
case class Mustang(color: String) extends Make { def race() { println( "Rrrroar" )}}

case class Car[M <: Make, F <: Fuel](make: M, fuel: F) {
   def refuel(f: F): Car[M, F] = copy(make, f)
}

case class Garage[M <: Make](cars: Seq[Car[M,_]] = Seq.empty) {
   def add(c: Car[M,_]) = copy(cars :+ c)
   def remove(c: Car[M,_]) = copy(cars.filterNot(_ == c))
   def refuel[F <: Fuel](c: Car[M,F], f: F) = copy( cars.map {
      case `c` => c.refuel(f)
      case other => other
   })
}    

val g0 = Garage[Mustang]()
val m  = Car(Mustang("black"), Benzin())
val f  = Car(Ferrari("red"), Benzin())
val g1 = g0.add(f)                // forbidden
val g1 = g0.add(m)                // ok
val g2 = g1.refuel(f, Benzin(45)) // forbidden
val g2 = g1.refuel(m, Diesel(45)) // forbidden
val g2 = g1.refuel(m, Benzin(45)) // ok
g2.cars.foreach(_.make.race())    // ok

結論:傍観しないでください...

ここに画像の説明を入力してください

于 2012-06-30T23:25:21.650 に答える