I ont ces modèles:types résolutives en polymorphisme borné 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() }
Si j'obtiens une instance d'un Vehicle[Car]
et invoque update()
, je vais obtenir un Car
. Depuis Car
étend Vehicle[Car]
(ou simplement, voiture est un véhicule [Car]), je peux en toute sécurité définir le type du résultat explicitement Vehicle[Car]
:
val car = new Car
val anotherCar = car.update()
val anotherCarAsVehicle: Vehicle[Car] = car.update() // works as expected
Mais si je veux, par exemple, mettre des instances de Car
et Bus
en une seule liste, je dois définir le type de liste à Vehicle[_ <: Vehicle[_]]
(ayant une liste de simplement Vehicle[_]
et invoquant update()
sur un élément produirait Any
, mais je veux être en mesure d'utiliser update()
donc je dois utiliser le type F-borné). L'utilisation de vis types existentiels les des relations de type, car une fois que je vais chercher la voiture sous-jacente/bus du véhicule, je ne peux plus lancer à des véhicules parce que ... eh bien, il est juste un certain type existentiel:
val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)
val car = seq.head.update()
val carAsVehicle: Vehicle[_ <: Vehicle[_]] = seq.head.update() // fails to compile
Alors , Vehicle
est paramétré avec un certain type T
qui est un sous-type de Vehicle[T]
. Lorsque je déchire le T
(en utilisant update()
), dans le cas de types de béton c'est ok - p. si je déchire le Car
, je peux affirmer sans risque que j'ai arraché un Vehicle[Car]
parce que Car <: Vehicle[Car]
. Mais si je déchire un type existentiel, je ne peux rien faire avec. L'exemple précédent a fonctionné parce que Car
est un Vehicle[Car]
, mais dans ce cas _
n'est pas un Vehicle[_]
.
Pour spécifier ma question concrète: pour les modèles donnés ci-dessus (Véhicule, Voiture, Bus), y at-il un moyen d'y parvenir?
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
Notez que vous pouvez changer les traits donnés, des classes et le type de seq
, mais il y a une restriction: update()
doit retourner T
, pas Vehicle[T]
. Je sais que l'utilisation de HList
sans forme permettrait de résoudre le problème car je n'aurais pas besoin d'utiliser des types existentiels (j'aurais simplement une liste de voiture et de bus, et cette information de type serait préservée). Mais je me demande pour ce cas d'utilisation particulier avec un simple List
.
EDIT:
@RomKazanova oui, ça marcherait bien sûr, mais je dois conserver le même type avant et après update()
(ici un upvote est pour l'effort que;)). Je crois que ce n'est pas possible sans HList ou une structure de données similaire, car l'unification des voitures et des bus nous oblige à utiliser le type de véhicule qui perd de l'information si son type sous-jacent était Car, Bus ou autre (tout ce que nous pouvons savoir qu'il s'agissait d'un type _ <: Vehicle
). Mais je veux vérifier avec vous les gars.
j'aurais pensé que 'Liste [des véhicules [_ <: véhicule [_]]]' est du même type que 'Liste [des véhicules [ T] forSome {type T <: Véhicule [T]}] '. En règle générale, cela ne me dérangerait pas d'utiliser 'forSome' partout, mais j'ai entendu dire qu'il est expulsé dans Scala 2.13 ou 2.14. Quoi qu'il en soit, merci beaucoup, c'est exactement le genre de solution que j'espérais. – slouc
J'ai élidé tous les avertissements de type existentiel de ma transcription REPL. Donc je pense que c'est le genre d'existentiel qui est inexprimable avec seulement des jokers. Je ne suis pas sûr * si * il sera complètement supprimé, mais si oui alors je * pense * le plus tôt serait quand Dotty devient Scala 3.0 ou quelque chose comme ça. –
BTW J'ai accidentellement downvoted vous :) fixe – slouc