Scala:通过 if 语句更正表示类型的类型推断 [英] Scala: Correcting type inference of representation type over if statement

查看:34
本文介绍了Scala:通过 if 语句更正表示类型的类型推断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是两个的后续问题 关于表示类型,它们是设计用于表示有界类型成员(或类似的东西)底层类型的特征的类型参数.我已经成功地创建了类的实例,例如 ConcreteGarage,它具有有界类型成员 CarType 的实例 cars.

This is a follow-up to two questions on representation types, which are type parameters of a trait designed to represent the type underlying a bounded type member (or something like that). I've had success creating instances of classes, e.g ConcreteGarage, that have instances cars of bounded type members CarType.

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

我可以轻松创建 Car 的实例,例如 FerrariMustang 并将它们放入 ConcreteGarage,只要简单:

I can easily create instances of Cars like Ferraris and Mustangs and put them into a ConcreteGarage, as long as it's simple:

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

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

然而,如果我只是根据标志返回一个或另一个,并尝试将结果放入车库,它会失败:

However, if I merely return one or the other, based on a flag, and try to put the result into a garage, it fails:

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

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

单独的开关工作正常,它是对 ConcreteGarage 构造函数的调用失败并出现相当神秘的错误:

The switch alone works fine, it is the call to ConcreteGarage constructor that fails with the rather mystical error:

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]] 表示类型参数放在任何地方,但没有成功找到魔点.

I have tried putting those magic [C <: Car[C]] representation type parameters everywhere, but without success in finding the magic spot.

推荐答案

FerrariMustang 没有任何有用的超类型可以别名.你需要用这种方法把世界从里到外包装起来.

There aren't any useful super types to which Ferrari and Mustang can be aliased. You need to wrap the world inside out with this approach.

一种可能性是将 Garage 构造作为一种方法添加到 Car 中.

One possibility is to add the Garage construction as a method to Car.

另一种可能性是定义一些世界",负责生产兼容的汽车和车库:

Another possibility is to define some 'world' which takes care of producing compatible cars and garages:

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)

<小时>

您可以看到这会导致幽闭恐惧症.所以这真的取决于你的目标场景.依赖路径的类型总是会导致未来的约束.考虑这个带有类型参数的相当简单的变体:


You can see that this can get quite claustrophobic. So it really depends on your target scenario. Path-dependent types always cause future constraints. Consider this rather simple variant with type parameters instead:

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

<小时>

结论:不要偏离轨道...


Conclusion: Don't get side-tracked...

这篇关于Scala:通过 if 语句更正表示类型的类型推断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆