Eug*_*ota 4 scala implicit typeclass
我想定义一个适用于所有类型的子类型的通用隐式转换器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) }
Run Code Online (Sandbox Code Playgroud)
上面的代码抛出一个错误:
error: could not find implicit value for parameter p: Price[FriedChicken]
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
^
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
更新:
正如@extempore指出的那样,错误的是我混淆了隐式转换(视图边界)和上下文边界(两者都使用了隐式参数).我的通用隐式转换器没有任何问题.问题是add使用上下文边界而不是视图.所以我们可以修复如下:
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit view: A => Price[A]) = (stuff, view(stuff)) :: list
Run Code Online (Sandbox Code Playgroud)
@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
}
Run Code Online (Sandbox Code Playgroud)
相关:
你真正想要的并不是很清楚.您确实混合了隐式转换和隐式参数.我没有尝试解决它,而是编写了一些代码.
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
//
Run Code Online (Sandbox Code Playgroud)