Je voudrais définir un convertisseur implicite générique qui fonctionne pour tous les sous-types de type T
. Par exemple:Scala: Convertisseurs implicites génériques?
abstract class Price[A] {
def price(a: Any): Int
}
trait Car
case class Prius(year: Int) extends Car
trait Food
case class FriedChicken() extends Food
object Def {
implicit def carToPrice[A <: Car](car: A): Price[A] = new Price[A] {
def price(car: Any) = 100
}
implicit def foodToPrice[A <: Food](food: A): Price[A] = new Price[A] {
def price(food: Any) = 5
}
// implicit object PriusPrices extends Price[Prius] {
// def price(car: Any) = 100
// }
//
// implicit object FriedChickenPrices extends Price[FriedChicken] {
// def price(food: Any) = 5
// }
}
import Def._
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit p: Price[A]) = (stuff, p) :: list
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
stuff map { x => x._2.price(x._1) }
Le code ci-dessus renvoie une erreur:
error: could not find implicit value for parameter p: Price[FriedChicken]
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
^
Qu'est-ce que je fais mal?
Mise à jour:
Comme @extempore souligné, quel est le problème est que je confonds les conversions implicites (bornes de vue) et les limites de contexte (à la fois utiliser des paramètres implicites). Il n'y a rien de mal avec mes convertisseurs implicites génériques. Le problème est que add
utilise des limites de contexte au lieu d'une vue. On peut donc le fixer comme suit:
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit view: A => Price[A]) = (stuff, view(stuff)) :: list
Une @extempore chose intéressante montre dans son code est que nous ne avons pas vraiment besoin d'un convertisseur générique si Price[A]
était contravariant. Fondamentalement, je peux faire Price[Car]
travailler au nom de Price[Prius]
, ce qui est un peu ce que je voulais. Ainsi, la version alternative est lié au contexte:
abstract class Price[-A] {
def price(a: Any): Int
}
implicit object CarPrice extends Price[Car] {
def price(a: Any) = 100
}
implicit object FoodPrice extends Price[Food] {
def price(a: Any) = 1
}
connexes:
Je veux 1) faire face à la conversion implicite pour une famille de type en une seule fois 2) stocker divers objets supportant la classe de types dans un conteneur et l'utiliser plus tard. Je n'ai pas pensé à utiliser la contravariance, mais cela a tout à fait du sens alors 'Price [Car]' peut agir comme 'Price [Prius]'. –