2011-08-06 20 views
6

与えられた型のデフォルト値を提供するtypeclass 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]) 
    } 
} 

予想通り上記の実装は、それが代わりにnullを与えるBigIntBigDecimalの例(とNumericのインスタンスである他のユーザー定義型)を除き、振る舞いゼロのforNumericforAnyRefよりも優先され、私が期待している動作が得られるように、どうすればよいですか?

答えて

11

それはスカラ参照の§6.26.3「オーバーロード解決方法」に係るforNumericよりより特定あるため暗黙forAnyRefが選択されます。そこのように、Defaultが延び形質にそれを移動することによって、その優先順位を軽減する方法です:

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

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

} 

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

は、しかし、今の両方forAnyRefforNumericが互いに限り具体的であるため、それは、トリックの一部だけだし、あいまいな暗黙のエラーが発生します。何故ですか?さて、forAnyRefは、AA >: 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) 

、この追加の制約はNumericのタイプのforAnyRefが使用可能であることforNumericValforNumericRefをより具体的になります。答えは、ジャンのため

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]) 
    } 
} 
+0

ありがとう:ここ – missingfaktor

6

は、問題を解決するための別の方法で、任意のコードの重複を必要としません。あなたのソリューションを調整する私はこの小さなコードの重複を避ける別のソリューションを考え出しました。私は読者の利益のためにそれを下に掲示しています。
関連する問題