解决F-bounded多态性中的类型

时间:2017-02-24 10:30:27

标签: scala generics f-bounded-polymorphism

我有这些模特:

trait Vehicle[T <: Vehicle[T]] { def update(): T }
class Car extends Vehicle[Car] { def update() = new Car() }
class Bus extends Vehicle[Bus] { def update() = new Bus() }

如果我获得Vehicle[Car]的实例并调用update(),我将获得Car。由于Car扩展Vehicle[Car](或简单地说,Car Vehicle [Car]),我可以安全地将结果的类型设置为Vehicle[Car]

val car = new Car
val anotherCar = car.update()
val anotherCarAsVehicle: Vehicle[Car] = car.update() // works as expected

但是,如果我想将CarBus的实例放在一个列表中,那么我必须将列表类型设置为Vehicle[_ <: Vehicle[_]](列出简单的列表) Vehicle[_]并在元素上调用update()会产生Any,但我希望能够使用update(),因此我必须使用F-bounded类型)。使用存在性类型搞定了类型关系,因为一旦我从Vehicle获取底层汽车/公共汽车,我就不能再把它投射到车辆上了,因为......好吧,它只是一些存在类型:

val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)
val car = seq.head.update()
val carAsVehicle: Vehicle[_ <: Vehicle[_]] = seq.head.update() // fails to compile

因此,Vehicle使用某种类型T进行参数化,Vehicle[T]T的子类型。当我撕掉update()(使用Car)时,如果是具体的类型,它就可以了 - 例如如果我删除Vehicle[Car],我可以安全地声称我删除了Car <: Vehicle[Car]因为Car。但如果我撕掉一个存在主义类型,我就无法做任何事情。之前的示例有效,因为Vehicle[Car]_,但在这种情况下,Vehicle[_]不是def sameType[T, U](a: T, b: U)(implicit evidence: T =:= U) = true val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus) sameType(seq.head.update +: seq.tail, seq) // true

要为上面给出的模型(车辆,汽车,公共汽车)指定我的具体问题,有没有办法实现这一目标?

seq

请注意,您可以更改update()的给定特征,类和类型,但有一个限制:T 必须返回Vehicle[T],而不是{{ 1}}

我知道使用无形HList可以解决问题,因为我不必使用存在类型(我只需要一个汽车和公共汽车列表,并保留类型信息)。但是我想通过一个简单的List来了解这个特定的用例。

编辑

@RomKazanova是的,这当然会有效,但我需要在update()之前和之后保留相同的类型(尽管这里是努力的赞成;))。

我认为没有HList或类似的数据结构是不可能的,因为统一汽车和公共汽车会迫使我们使用车辆类型,这种车型失去了关于其底层类型是汽车,公共汽车还是其他东西的信息(我们都是可以知道它是某种类型_ <: Vehicle)。但我想和你们一起检查一下。

2 个答案:

答案 0 :(得分:4)

我对存在类型不是很好,所以我无法解释太多:-p但是当你将seq的类型改为List[Vehicle[T] forSome {type T <: Vehicle[T]}]时到&#34;锻炼&#34;。请注意,您必须将类型传递给List构造函数/ apply方法。

scala> val seq = List[Vehicle[T] forSome {type T <: Vehicle[T]}](new Car, new Bus)
seq: List[Vehicle[T] forSome { type T <: Vehicle[T] }] = List(Car@31e53802, Bus@54d569e7)

scala> sameType(seq.head.update +: seq.tail, seq)
res3: Boolean = true

scala> seq.head.update
res4: T forSome { type T <: Vehicle[T] } = Car@79875bd2

scala> seq.head.update.update
res5: T forSome { type T <: Vehicle[T] } = Car@6928c6a0

scala> new Car +: seq
res6: List[Vehicle[T] forSome { type T <: Vehicle[T] }] = List(Car@51f0a09b, Car@31e53802, Bus@54d569e7)

我认为摆脱这个答案的主要原因是,这可以让你拼出Vehicle类型构造函数的递归性质。

我不确定我会推荐这个......

答案 1 :(得分:2)

有两种解决方法:

val carAsVehicle: Vehicle[_] = seq.head.update()

或使用模式匹配

val carAsVehicle: Vehicle[Car] = seq.head match {
  case car: Vehicle[Car] => car.update()
}

但有趣的是:

val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)

val vehicleAsVihecles: List[Vehicle[_]]= seq.map(_.update()) // compiled

val vehicleAsVihecles1: List[Vehicle[_ <: Vehicle[_]]]= seq.map(_.update()) //not compiled

def somedef(vehicles: List[Vehicle[_ <: Vehicle[_]]]) = vehicles.map(_.update()) //compiled
somedef(seq)