I am trying to create a typeclass Default that supplies the default value for a given type. Here is what I have come up with so far:
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])
}
}
The above implementation behaves as expected, except for the cases of BigInt and BigDecimal (and other user defined types that are instances of Numeric) where it gives null instead of zero. What should I do so that forNumeric takes precedence over forAnyRef and I get the behavior I expect?
The
forAnyRefimplicit is chosen because it is more specific thanforNumericaccording to §6.26.3 “Overloading Resolution” of the Scala reference. There is a way to reduce its priority by moving it to a trait thatDefaultextends, like this:But that’s only part of the trick, because now both
forAnyRefandforNumericare as specific as each other, and you’ll get an ambiguous-implicit error. Why is that? Well,forAnyRefgets an extra specificity point because it has a non-trivial constraint onA:A >: Null. What you can do then, to add a nontrivial constraint toforNumeric, is to double it inDefault:Now, this additional constraint makes
forNumericValandforNumericRefmore specific thatforAnyReffor types where aNumericis available.