Scala: Общие неявные преобразователи? - PullRequest
4 голосов
/ 02 октября 2010

Я хотел бы определить общий неявный преобразователь, который работает для всех подтипов типа 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
}

Related :

Ответы [ 3 ]

6 голосов
/ 02 октября 2010

Не очень понятно, чего вы действительно хотите.Вы действительно смешиваете неявные преобразования и неявные параметры.Вместо того, чтобы пытаться разобраться, я написал код.

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
// 
2 голосов
/ 02 октября 2010

Проблема в том, что вы определили ваши неявные carToPrice и foodToPrice как неявные методы от значений Car и Food до Price с, но не Carи Food значения являются доказательством того, где вам нужны преобразования.Поскольку вы на самом деле не используете аргументы в этих неявных методах, я думаю, что вы действительно хотите неявные значения , например:

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 голосов
/ 02 октября 2010

Как указал @ extempore , неправильно то, что я путаю неявные преобразования (границы просмотра) и границы контекста (оба используют неявные параметры).Нет ничего плохого в моих общих неявных преобразователях.Проблема в том, что add использует границы контекста вместо представления.Таким образом, мы можем исправить это следующим образом:

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