6

私は次の一般的なインターバルクラスを持っています(ユーザーsocによって親切に作成されました):

case class Interval[T](from: T, to: T)(implicit num: Numeric[T]) {
  import num.mkNumericOps // allows us to write from.toDouble and to.toDouble
  def mid: Double = (from.toDouble + to.toDouble) / 2.0
}

典型的なユースケース:Interval[Double]またはInterval[Int]。バイナリ和集合演算子と共通(implicit num: Numeric[T])部分演算子を追加するために、コンパニオンオブジェクトと同様のパターンに従いました。

object Interval {

  def union[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
    import num.mkOrderingOps // allows interval1.from min
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)
  }

  def intersect[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
    import num.mkOrderingOps
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
  }

}  

(implicit num: Numeric[T])import num.mkOrderingOps両方のメソッドの内部をコピーするのは醜い定型文です。Intervalオブジェクト自体のレベルで、これを1回だけ実行する方法はありますか?

4

2 に答える 2

8

はいあります。

まずインポートします。代わりに、IntervalのスコープにOrdering.Implicits._をインポートできます。

object Interval {
   import Ordering.Implicits._

   def union[T](....)(implicit num: Numeric[T]) = {
     // do not import num.mkOrderingOps
     ...
   }
   ...
}

これらの暗黙的機能を使用すると、順序付け操作が見つかると、操作が発生するスコープ内で暗黙的順序付け(数値は順序付け)が検索されます。そして、各ルーチンのスコープには、たまたま1つの適切な暗黙があります。算術演算も必要な場合は、Numeric.Implicits._もインポートしてください。

今、暗黙の引数で。def f[T: X](args)そのための言語には、コンテキストバウンドと呼ばれるショートカットがあります。def f[T](args)(implicit someName: X[T])

違いは、コンテキストがバインドされた暗黙の名前がないことです(implictly [T]を使用できますが、これはほとんど短くなりません。幸い、インポートOrdering.Implicits._では名前は不要になります。

それで

object Interval {
   import Ordering.Implicits._
   // also import Numeric.Implicits._ if you need +,-,*,/ ...
   def union[T: Numeric] ...
   def intersection[T: Numeric] ...
}
于 2012-05-13T08:50:58.543 に答える
6

Numericオブジェクトでの型クラスの使用には、囲んでいるスコープのどこかにバインドする必要がIntervalある型パラメーターがあります。は一意の定数値であるため、そのバインディングを提供できません。TInterval

この特定の問題に対する1つの解決策はunionintersect操作の定義を通常のインスタンスメソッドとしてInterval クラスに移動することです。この場合、それらはクラスの残りの部分とのバインディングTと関連するインスタンスを共有します。Numeric

case class Interval[T : Numeric](from: T, to: T) {
  import Numeric.Implicits._
  import Ordering.Implicits._

  def mid: Double = (from.toDouble + to.toDouble) / 2.0
  def union(interval2: Interval[T]) =
    Interval(this.from min interval2.from, this.to max interval2.to)
  def intersect(interval2: Interval[T]) =
    Interval(this.from max interval2.from, this.to min interval2.to)
}

ただし、これらの操作の定義をクラスから分離しIntervalたい場合は、APIを追跡する必要がある暗黙のボイラープレートの量を減らすための1つのアプローチは、Numeric[の観点から独自の高レベルの型クラスを定義することです。 T]。例えば、

// Type class supplying union and intersection operations for values
// of type Interval[T]
class IntervalOps[T : Numeric] {
  import Ordering.Implicits._

  def union(interval1: Interval[T], interval2: Interval[T]) =
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)

  def intersect(interval1: Interval[T], interval2: Interval[T]) =
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
}

implicit def mkIntervalOps[T : Numeric] = new IntervalOps[T]

使用中は次のようになります。

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : IntervalOps[T]) = {
  import ops._
  val i3 = union(i1, i2)
  val i4 = intersect(i1, i2)
  (i3, i4)
}

3番目のオプションは、これら2つを組み合わせ、暗黙的な定義を使用して、元のクラスを追加のメソッドで強化します。

class IntervalOps[T : Numeric](interval1 : Interval[T]) {
  import Ordering.Implicits._

  def union(interval2: Interval[T]) =
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)

  def intersect(interval2: Interval[T]) =
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
}

implicit def enrichInterval[T : Numeric](interval1 : Interval[T]) =
  new IntervalOps[T](interval1)

type Ops[T] = Interval[T] => IntervalOps[T]

その後、使用中、

def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : Ops[T]) = {
  val i3 = i1 union i2
  val i4 = i1 intersect i2
  (i3, i4)
}
于 2012-05-13T08:46:30.983 に答える