I would like to define a generic implicit converter that works for all subtypes of type T
. For example:
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) }
The above code throws an error:
error: could not find implicit value for parameter p: Price[FriedChicken]
val stuff = add(Prius(2000), add(FriedChicken(), Nil))
^
What am I doing wrong?
Update:
As @extempore pointed out, what's wrong is that I am confusing implicit conversions (view bounds) and context bounds (both make use of implicit parameters). There's nothing wrong with my generic implicit converters. The problem is that add
is using context bounds instead of a view. So we can fix it as follows:
def add [A, B >: A](stuff: A, list: List[(B, Price[_])])(implicit view: A => Price[A]) = (stuff, view(stuff)) :: list
An interesting thing @extempore demonstrates in his code is that we don't really need a generic converter if Price[A]
was contravariant. Basically, I can make Price[Car]
work on behalf of Price[Prius]
, which is kind of what I wanted. So the alternative context bound version is:
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
}
Related:
Implicit conversions in Scala are the set of methods that are apply when an object of wrong type is used. It allows the compiler to automatically convert of one type to another. Implicit conversions are applied in two conditions: First, if an expression of type A and S does not match to the expected expression type B.
Scala will first look for implicit definitions and implicit parameters that can be accessed directly (without a prefix) at the point the method with the implicit parameter block is called. Then it looks for members marked implicit in all the companion objects associated with the implicit candidate type.
An implicit conversion from type S to type T is defined by an implicit value which has function type S => T , or by an implicit method convertible to a value of that type. Implicit conversions are applied in two situations: If an expression e is of type S , and S does not conform to the expression's expected type T .
An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.
It's not very clear what you really want. You are indeed mixing up implicit conversions and implicit parameters. Rather than try to sort it out I wrote some 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
//
The problem is that you've defined your implicit carToPrice
and foodToPrice
as implicit methods from a Car
and Food
values to Price
s, yet no Car
and Food
values are in evidence where you need the conversions. Since you don't actually use the arguments in these implicit methods, what I think you really want is implicit values, like so:
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With