回答:
これはすでに尋ねられたと思いましたが、そうだとすれば、「関連」バーに質問が表示されません。だから、ここにあります:
バインドビューは、いくつかの種類の使用を可能にするために、Scalaで導入された仕組みだったA
かのように、それはいくつかのタイプでしたB
。典型的な構文はこれです:
def f[A <% B](a: A) = a.bMethod
言い換えると、タイプのオブジェクトでメソッドを呼び出すことができるようにA
、暗黙的にB
利用可能に変換する必要があります。標準ライブラリ(とにかくScala 2.8.0より前)でのビュー境界の最も一般的な使用法は、次のようにで使用されます。B
A
Ordered
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b
一方が変換できるためA
にOrdered[A]
、そしてためOrdered[A]
の方法を定義し<(other: A): Boolean
、Iは式を使用することができますa < b
。
ビューの境界は非推奨であることに注意してください。これらは避けてください。
コンテキスト境界はScala 2.8.0で導入されたもので、通常、いわゆる型クラスパターン(Haskell型クラスによって提供される機能をより詳細な方法でエミュレートするコードのパターン)で使用されます。
ビューの境界は単純な型(などA <% String
)で使用できますが、コンテキストの境界には上記のようなパラメーター化された型が必要ですOrdered[A]
が、とは異なりString
ます。
コンテキストバウンドは、ビューバウンドの暗黙的な変換ではなく、暗黙的な値を表します。これは、一部のtypeについて、利用可能なtypeの暗黙的な値があることを宣言するために使用されます。構文は次のようになります。A
B[A]
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]
これは、使用方法がすぐに明確ではないため、ビューバウンドよりも混乱します。Scalaでの一般的な使用例は次のとおりです。
def f[A : ClassManifest](n: Int) = new Array[A](n)
Array
パラメータ化された型の初期化では、ClassManifest
型の消去と配列の非消去の性質に関連する難解な理由により、が使用可能である必要があります。
ライブラリのもう1つの非常に一般的な例は、もう少し複雑です。
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
ここでimplicitly
は、必要な暗黙の値(typeの1つ)を取得するために使用されます。Ordering[A]
このクラスはメソッドを定義しcompare(a: A, b: A): Int
ます。
これを行う別の方法を以下に示します。
ビューの境界とコンテキストの境界の両方が、その定義を前提として、暗黙のパラメーターで実装されていることは驚くべきことではありません。実際、私が示した構文は、実際に何が起きるかを表す構文上の糖衣です。彼らが砂糖を取り除く方法を以下に示します:
def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod
def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)
したがって、当然のことながら、完全な構文でそれらを記述できます。これは、コンテキストの境界に特に役立ちます。
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
ビューの境界は、主にpimp myライブラリパターンを利用するために使用されます。これにより、元の型を何らかの方法で返したい場合に、既存のクラスにメソッドを「追加」します。なんらかの方法でその型を返す必要がない場合は、ビューのバインドは必要ありません。
ビューにバインドされた使用法の典型的な例は、の処理Ordered
です。は暗黙的な変換がありますが、たとえばでInt
はないことに注意してくださいOrdered
。前に示した例は、変換されていない型を返すため、ビューバインドが必要です。
def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b
この例は、ビュー境界がないと機能しません。ただし、別のタイプを返す場合、ビューをバインドする必要はもうありません。
def f[A](a: Ordered[A], b: A): Boolean = a < b
ここでの変換(必要な場合)は、パラメーターをに渡す前に行われるためf
、そのf
ことを知る必要はありません。
加えてOrdered
、ライブラリからの最も一般的な使用法は、Scalaコレクションと同様に、JavaクラスであるString
およびの処理Array
です。例えば:
def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b
一つのビュー際限なくこれを行うことを試みた場合、の戻り型がString
なりWrappedString
、同様のため(スカラ2.8) Array
。
型が戻り型の型パラメーターとしてのみ使用されている場合でも、同じことが起こります。
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted
コンテキスト境界は、主にHaskellの型クラスへの参照として、typeclass patternと呼ばれるもので使用されます。基本的に、このパターンは、一種の暗黙的なアダプターパターンを介して機能を使用可能にすることにより、継承の代替手段を実装します。
典型的な例はScala 2.8でOrdering
、Ordered
Scalaのライブラリ全体で置き換えられています。使用法は次のとおりです。
def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b
ただし、通常は次のように記述されています。
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
import ord.mkOrderingOps
if (a < b) a else b
}
これはOrdering
、従来の演算子スタイルを可能にする内部の暗黙的な変換を利用しています。Scala 2.8のもう1つの例は次のNumeric
とおりです。
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
より複雑な例は、の新しいコレクションの使用法ですCanBuildFrom
が、これについては非常に長い回答がすでにあるため、ここではそれを避けます。そして、前述のように、ClassManifest
具体的な型なしで新しい配列を初期化するために必要な使用法があります。
typeclassパターンにバインドされたコンテキストは、懸念の分離を可能にするため、独自のクラスで使用される可能性がはるかに高くなります。一方、ビューの境界は、適切な設計によって独自のコードで回避できます(主に他の誰かの設計を回避するために使用されます) )。
長い間可能でしたが、コンテキストの境界の使用は2010年に本格的に始まり、現在、Scalaの最も重要なライブラリとフレームワークのほとんどである程度使用されています。ただし、その最も極端な使用例は、Scalazライブラリであり、Haskellの多くの機能をScalaにもたらします。型クラスのパターンを読んで、それを使用できるすべての方法をよく理解することをお勧めします。
編集
関心のある関連質問: