9

型の上限と比較するときに、Scala で型クラスを使用する動機付けに問題があります。

次のコードを検討してください。

  case class NumList[T <: Complex](xs: Complex*) {
    def sum = (xs fold new Complex(0, 0))(_ + _)
    def map[U <: Complex](f: Complex => U): NumList[U] = NumList(xs.map(f): _*)
    override def toString = "[" + xs.mkString(", ") + "]"
  }

  case class GenList[T](xs: T*) {
    def sum(implicit num: Numeric[T]) = xs.sum
    def map[U](f: T => U) = GenList(xs.map(f): _*)
    override def toString = "[" + xs.mkString(", ") + "]"
  }

  val r = new Real(2)
  val n = new Natural(10)
  val comps = NumList(r, n, r, n)

  println(comps)
  println("sum: " + comps.sum)
  println("sum * 2: " + comps.map(x => x + x).sum)

  val comps2 = GenList(4, 3.0, 10l, 3d)
  println(comps2)
  println("sum: " + comps2.sum)
  println("sum * 2: " + comps2.map(_ * 2).sum)

これら 2 つのリストは同様の問題を解決しますが、一方は数値型クラスを使用し、もう一方は型パラメーターの上限を使用します。技術的な違いはよく理解できますが、型クラスの核となる動機にたどり着くのに苦労しています。これまでに見つけた最高の動機は次のとおりです。

インターフェイスをサブクラス化または実装すると、ほとんど同じ設計を行うことができますが、型クラスを使用すると、メソッドごとに型の機能を指定できますが、型Tと上限を持つジェネリック クラスは、それが使用されるすべての場所でU制約を受けます。Tこれを念頭に置いて、型クラスはジェネリック クラスの T の機能をよりきめ細かく制御します。

パターンの動機となる非常に明確な例はありますか?

4

1 に答える 1

9

1 つの主要な側面を単純化しようとして、型クラスはクラス階層とは独立して動作を収集しようとします。

新しい数値型を (標準の数値演算で) 定義する必要があるが、何らかの理由でMetaNumそれを自分の型のサブクラスにすることができない、または作成しないとします。Complex

型クラスを使用すると、必要な操作を提供Numericして、 に適切なインスタンスを提供するだけで済みますMetaNum

次に、 を作成しGenList[MetaNum]て合計することができます。

は ではないNumListため、 でこれを行うことはできません。定義時に行った実装の選択は、次の瞬間に操作/データ構造を一般化しようとしたときに突き刺さります。MetaNumComplexNumList

結論
型クラスを使用すると、階層的な考慮事項とは別に動作を自由に拡張できますが、複雑さとボイラープレートがいくらか追加されます。

あなたの質問で同じことを意味しているかどうかはわかりません。

于 2013-04-10T13:54:22.513 に答える