解析 F-bounded 多态性中的类型
Resolving types in 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
但是,如果我想将 Car
和 Bus
的实例放在一个列表中,那么我必须将列表类型设置为 Vehicle[_ <: Vehicle[_]]
(有一个列表简单地 Vehicle[_]
并在元素上调用 update()
会产生 Any
,但我希望能够使用 update()
,所以我必须使用 F-bounded 类型)。使用存在类型搞砸了类型关系,因为一旦我从 Vehicle 中获取底层 car/bus,我就不能再将它转换为 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[_]
。
具体说明我的具体问题:对于上面给出的模型(Vehicle,Car,Bus),有没有办法实现这个?
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()
必须 return T
,不是 Vehicle[T]
.
我知道使用 shapeless HList
可以解决问题,因为我不必使用存在类型(我只需要一个汽车和公共汽车的列表,并且该类型信息将被保留).但是我想知道这个特殊的用例有一个简单的 List
.
编辑:
@RomKazanova 是的,这当然可行,但我需要在 update()
之前和之后保留相同的类型(不过,这是对这项工作的赞成票 ;))。
我认为如果没有 HList 或类似的数据结构,这是不可能的,因为统一汽车和公共汽车迫使我们使用车辆类型,这会丢失有关其基础类型是汽车、公共汽车还是其他类型的信息(我们只知道是它是某种类型 _ <: Vehicle
)。但我想和你们确认一下。
有两种解决方法:
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)
我不太擅长存在类型,所以我不能对此解释太多:-p 但是当你将 seq
的类型更改为 List[Vehicle[T] forSome {type T <: Vehicle[T]}]
时,一切似乎 "work out"。请注意,您必须将类型传递给 List
constructor/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
类型构造函数的递归性质。
虽然我不确定我会推荐这个...
我有这些型号:
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
但是,如果我想将 Car
和 Bus
的实例放在一个列表中,那么我必须将列表类型设置为 Vehicle[_ <: Vehicle[_]]
(有一个列表简单地 Vehicle[_]
并在元素上调用 update()
会产生 Any
,但我希望能够使用 update()
,所以我必须使用 F-bounded 类型)。使用存在类型搞砸了类型关系,因为一旦我从 Vehicle 中获取底层 car/bus,我就不能再将它转换为 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[_]
。
具体说明我的具体问题:对于上面给出的模型(Vehicle,Car,Bus),有没有办法实现这个?
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()
必须 return T
,不是 Vehicle[T]
.
我知道使用 shapeless HList
可以解决问题,因为我不必使用存在类型(我只需要一个汽车和公共汽车的列表,并且该类型信息将被保留).但是我想知道这个特殊的用例有一个简单的 List
.
编辑:
@RomKazanova 是的,这当然可行,但我需要在 update()
之前和之后保留相同的类型(不过,这是对这项工作的赞成票 ;))。
我认为如果没有 HList 或类似的数据结构,这是不可能的,因为统一汽车和公共汽车迫使我们使用车辆类型,这会丢失有关其基础类型是汽车、公共汽车还是其他类型的信息(我们只知道是它是某种类型 _ <: Vehicle
)。但我想和你们确认一下。
有两种解决方法:
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)
我不太擅长存在类型,所以我不能对此解释太多:-p 但是当你将 seq
的类型更改为 List[Vehicle[T] forSome {type T <: Vehicle[T]}]
时,一切似乎 "work out"。请注意,您必须将类型传递给 List
constructor/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
类型构造函数的递归性质。
虽然我不确定我会推荐这个...