2010-10-01 8 views
4

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:

Répondre

6

Il est pas très clair ce que vous voulez vraiment. Vous mélangez en effet des conversions implicites et des paramètres implicites. Plutôt que d'essayer de le trier, j'ai écrit du code.

object Test { 
    type Price = Int 
    abstract class Pricable[-A] { 
    def price(a: A): Price 
    } 

    trait Car 
    case class Prius(year: Int) extends Car 
    trait Food 
    case class FriedChicken() extends Food 

    implicit val CarPricingGun = new Pricable[Car] { 
    def price(a: Car): Price = 100 
    } 
    implicit val FoodPricingGun = new Pricable[Food] { 
    def price(a: Food): Price = 1 
    } 
    implicit def priceableItemToPrice[A: Pricable](x: A) = 
    implicitly[Pricable[A]] price x 

    def main(args: Array[String]): Unit = { 
    val x1 = Prius(2000) 
    val x2 = FriedChicken() 

    println("Price of " + x1 + " is " + (x1: Price)) 
    println("Price of " + x2 + " is " + (x2: Price)) 
    } 
} 
// Output is: 
// 
// Price of Prius(2000) is 100 
// Price of FriedChicken() is 1 
// 
+0

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]'. –

2

Le problème est que vous avez défini votre implicite carToPrice et foodToPricecomme implicites méthodes à partir de Car et Food valeurs à Price s, mais aucune valeur Car et Food sont en évidence où vous avez besoin des conversions. Puisque vous n'utilisez pas réellement les arguments dans ces méthodes implicites, ce que je pense que vous voulez vraiment est implicite des valeurs, comme ceci:

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 
} 
+0

Y compris les aliments en conversion ne pas le résoudre si: foodToPrice def implicite [A <: Food] (alimentation: A): Prix [A] = new Prix [ a] { prix def (a: Tous) = correspondance alimentaire { cas poulet: poulet frit => 2 cas _ => 1 } } –

+0

Ma suggestion est que vous * ne * dépendez un argument alimentaire (Je l'ai commenté dans ma réponse). –

+0

Si vous voulez faire correspondre le motif, faites-le sur l'argument de 'price'. –

0

Comme @extempore a 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 fixer comme suit: semble

def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit view: A => Price[A]) = (stuff, view(stuff)) :: list 
Questions connexes