2010-10-01 81 views
4

我想定义一个适用于所有类型为T的子类型的通用隐式转换器。例如:Scala:通用隐式转换器?

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) } 

上面的代码抛出一个错误:

error: could not find implicit value for parameter p: Price[FriedChicken] 
     val stuff = add(Prius(2000), add(FriedChicken(), Nil)) 
            ^

我在做什么错?

更新

由于@extempore指出的,什么是错的是我混乱的隐式转换(查看边界)和上下文范围(包括使用隐含参数)。我的通用隐式转换器没有问题。问题是add正在使用上下文边界而不是视图。

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

一个有趣的事情@extempore表明了他的代码是,我们并不真正需要一个通用的转换器,如果Price[A]是逆变:所以我们可以如下修复它。基本上,我可以让Price[Car]代表Price[Prius]工作,这是我想要的。所以,替代背景下必然的版本是:

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 
} 

相关

回答

6

这不是很清楚你真正想要的。你确实混合了隐式转换和隐式参数。我没有尝试把它整理出来,而是编写了一些代码。

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

我想要1)在一次处理中处理类型族的隐式转换2)将支持类型类的各种对象存储在某个容器中并稍后使用它。我没有想到使用反变换,但它是完全合理的,所以'Price [Car]'可以作为'Price [Prius]'。 – 2010-10-02 04:55:20

2

的问题是,已经定义了你隐carToPricefoodToPrice隐式方法CarFood的值到Price s,但没有CarFood值是您需要转换的证据。因为你实际上并没有在这些隐含的方法使用的参数,我想你真正想要的是隐含的,就像这样:

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

包括食物转化似乎并没有解决它虽然:隐含def foodToPrice [A <:食物](食物:A):价格[A] =新价格[ A] { 高清价格(答:任何)=食品匹配{ 情况下鸡:炸鸡=> 2 情况_ => 1 }} 我 – 2010-10-01 23:58:54

+0

的建议是,你*不*依赖于食品的参数(我在我的回答中评论)。 – 2010-10-02 00:05:39

+0

如果您想要模式匹配,请在“price”参数上执行。 – 2010-10-02 00:17:07

0

由于@extempore指出的,什么是错的是我混乱的隐式转换(查看边界)和上下文范围(包括使用隐含参数)。我的通用隐式转换器没有问题。问题是add正在使用上下文边界而不是视图。所以我们可以解决它如下:

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