Неявное разрешение параметров - установка приоритета - PullRequest
6 голосов
/ 06 августа 2011

Я пытаюсь создать класс типов Default, который предоставляет значение по умолчанию для данного типа. Вот что я придумала до сих пор:

trait Default[A] {
  def value: A
}

object Default {
  def withValue[A](a: A) = new Default[A] {
    def value = a
  }

  def default[A : Default]: A = implicitly[Default[A]].value

  implicit val forBoolean = withValue(false)

  implicit def forNumeric[A : Numeric] = 
    withValue(implicitly[Numeric[A]].zero)

  implicit val forChar = withValue(' ')

  implicit val forString = withValue("")

  implicit def forOption[A] = withValue(None : Option[A])

  implicit def forAnyRef[A >: Null] = withValue(null : A)
}

case class Person(name: String, age: Int)

case class Point(x: Double, y: Double)

object Point {
  implicit val pointDefault = Default withValue Point(0.0, 0.0)
}

object Main {
  def main(args: Array[String]): Unit = {
    import Default.default
    println(default[Int])
    println(default[BigDecimal])
    println(default[Option[String]])
    println(default[String])
    println(default[Person])
    println(default[Point])
  }
}

Вышеприведенная реализация ведет себя как ожидалось, за исключением случаев BigInt и BigDecimal (и других пользовательских типов, которые являются экземплярами Numeric), где она дает null вместо нуля. Что я должен сделать, чтобы forNumeric имел приоритет над forAnyRef и я получил ожидаемое поведение?

Ответы [ 2 ]

13 голосов
/ 06 августа 2011

Неявное forAnyRef выбрано потому, что оно более конкретно , чем forNumeric в соответствии с §6.26.3 «Разрешение перегрузки» ссылки Scala.Есть способ уменьшить приоритет, переместив его в черту, которая расширяет Default, например:

trait LowerPriorityImplicits extends LowestPriorityImplicits {
  this: Default.type =>

  implicit def forAnyRef[A >: Null] = withValue(null: A)

}

object Default extends LowerPriorityImplicits {
  // as before, without forAnyRef
}

Но это только часть уловки, потому что теперь и forAnyRef, и forNumericспецифичны друг для друга, и вы получите двусмысленную неявную ошибку.Это почему?Ну, forAnyRef получает дополнительное очко специфичности, потому что имеет нетривиальное ограничение на A: A >: Null.Затем, чтобы добавить нетривиальное ограничение к forNumeric, вы можете удвоить его в Default:

implicit def forNumericVal[A <: AnyVal: Numeric] = withValue(implicitly[Numeric[A]].zero)

implicit def forNumericRef[A <: AnyRef: Numeric] = withValue(implicitly[Numeric[A]].zero)

Теперь это дополнительное ограничение делает forNumericVal и forNumericRef более конкретными, чемforAnyRef для типов, где доступно Numeric.

6 голосов
/ 06 августа 2011

Вот еще один способ решения проблемы, не требующий дублирования кода:

trait Default[A] {
  def value: A
}

object Default extends LowPriorityImplicits {
  def withValue[A](a: A) = new Default[A] {
    def value = a
  }

  def default[A : Default]: A = implicitly[Default[A]].value

  implicit val forBoolean = withValue(false)

  implicit def forNumeric[A : Numeric] = 
    withValue(implicitly[Numeric[A]].zero)

  implicit val forChar = withValue(' ')

  implicit val forString = withValue("")

  implicit def forOption[A] = withValue(None : Option[A])
}

trait LowPriorityImplicits { this: Default.type =>
  implicit def forAnyRef[A](implicit ev: Null <:< A) = withValue(null : A)
}

case class Person(name: String, age: Int)

case class Point(x: Double, y: Double)

object Point {
  implicit val pointDefault = Default withValue Point(0.0, 0.0)
}

object Main {
  import Default.default

  def main(args: Array[String]): Unit = {
    println(default[Int])
    println(default[BigDecimal])
    println(default[Option[String]])
    println(default[String])
    println(default[Person])
    println(default[Point])
  }
}
...