2017-02-24 91 views
5

解决类型我有这些模型:在F-界多态性

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](或简单地说,汽车是一个汽车[汽车]),我可以放心地明确设置结果的类型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-bound类型)。使用存在类型砸了类型的关系,因为一旦我从车辆获取潜在汽车/公交车,我再也不能将它转换为车辆因为......好吧,这只是一些存在类型:

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]。但是如果我剔除存在类型,我无法做任何事情。之前的例子工作,因为CarVehicle[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]

我知道使用无形HList可以解决这个问题,因为我不需要使用存在类型(我只是简单地列出了一辆汽车和一辆公交车,并且该类型的信息将被保留)。但是我想用这个简单的List这个特殊的用例。

编辑

@RomKazanova是,将工作,当然,但我需要之前和之后update()保留同一类型(这里所付出的努力的给予好评,虽然;))。

我相信,如果没有HList或类似的数据结构,这是不可能的,因为统一的汽车和公共汽车迫使我们使用车型,这种车型会丢失其底层类型是汽车,公交车还是别的东西(我们可以知道的是这是一些类型_ <: Vehicle)。但我想和你们核对一下。

回答

4

我不是很好的生存类型,所以我不能解释太多:-p但是,当您将seq的类型更改为List[Vehicle[T] forSome {type T <: Vehicle[T]}]一切似乎都“解决”了。请注意,您必须将类型传递给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([email protected], [email protected]) 

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

scala> seq.head.update 
res4: T forSome { type T <: Vehicle[T] } = [email protected] 

scala> seq.head.update.update 
res5: T forSome { type T <: Vehicle[T] } = [email protected] 

scala> new Car +: seq 
res6: List[Vehicle[T] forSome { type T <: Vehicle[T] }] = List([email protected], [email protected], [email protected]) 

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

我不知道我会建议,虽然这...

+0

我本来以为'列表[汽车[_ <:汽车[_]]]'是同一类型'名单[汽车[ T] forSome {type T <:Vehicle [T]}]'。作为一般的经验法则,我并不介意在任何地方使用'forSome',但我听说它被Scala 2.13或2.14驱逐。无论如何,非常感谢你,这正是我期待的那种解决方案。 – slouc

+0

我确实从我的REPL记录中删除了所有的存在类型警告。所以我认为这是一种只有通配符才能表达的存在感。我不确定*如果*它会被完全删除,但如果是的话,我认为最快的时候会是Dotty变成Scala 3.0或类似的东西。 –

+0

顺便说一句,我意外地低估了你:)固定 – slouc

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)