【问题标题】:Scala: Generic implicit converters?Scala:通用隐式转换器?
【发布时间】:2010-10-01 23:00:10
【问题描述】:

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

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
}

相关

【问题讨论】:

    标签: scala implicit typeclass


    【解决方案1】:

    不清楚你真正想要什么。您确实混淆了隐式转换和隐式参数。我没有尝试整理它,而是编写了一些代码。

    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
    // 
    

    【讨论】:

    • 我想 1) 一次性处理一系列类型的隐式转换 2) 将支持类型类的各种对象存储在某个容器中并稍后使用。我没想过使用逆变,但这完全有道理,所以Price[Car] 可以充当Price[Prius]
    【解决方案2】:

    问题是您已将隐式carToPricefoodToPrice 定义为从CarFood 值到Prices 的隐式方法,但没有@987654326 @ 和 Food 值是您需要转换的证据。由于您实际上并未在这些隐式方法中使用参数,因此我认为您真正想要的是隐式 values,如下所示:

    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
    }
    

    【讨论】:

    • 将食物包含在转换中似乎并没有解决它:隐式 def foodToPrice[A <: food a price new def a: any match case chicken: friedchicken=""> 2 case _ => 1 } }
    • 我的建议是您不要依赖于食物论点(我在回答中将其注释掉了)。
    • 如果你想进行模式匹配,请在price的参数上进行。
    • 我看到你的版本有效,但我不清楚为什么我的版本与 implicit def intToString(x: Int) = x.toString 有任何不同。
    • 我在问题的末尾添加了我认为正在发生的事情。
    【解决方案3】:

    正如@extempore 指出的那样,我混淆了隐式转换(视图边界)和上下文边界(两者都使用隐式参数)。我的通用隐式转换器没有任何问题。问题是add 使用的是上下文边界而不是视图。所以我们可以这样修复它:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 2018-09-16
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多