簡単な方法で、コンテキストとビューの境界とは何ですか?それらの違いは何ですか?
いくつかのわかりやすい例も素晴らしいでしょう!
私はこれはすでに尋ねられたと思ったが、もしそうなら、質問は「関連する」バーで明白ではない。だから、ここにあります:
ビューバウンドは、Scalaで導入されたメカニズムであり、何らかのタイプA
をifそれは何らかのタイプB
でした。典型的な構文は次のとおりです。
def f[A <% B](a: A) = a.bMethod
言い換えると、A
型のオブジェクトでB
メソッドを呼び出すことができるように、B
は暗黙的にA
に変換できる必要があります。標準ライブラリのビュー境界の最も一般的な使用方法(とにかくScala 2.8.0以前)は、次のようにOrdered
を使用します。
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b
A
をOrdered[A]
に変換でき、Ordered[A]
がメソッド<(other: A): Boolean
を定義しているため、式a < b
を使用できます。
view bounds is deprecated であることに注意してください。
コンテキスト境界はScala 2.8.0で導入され、通常、いわゆるtype class pattern、提供される機能をエミュレートするコードのパターンで使用されますHaskell型クラスによるものですが、より冗長な方法です。
ビューバインドは単純な型(たとえば、A <% String
)で使用できますが、コンテキストバインドには、上記のOrdered[A]
などのパラメーター化された型が必要です。ただし、String
とは異なります。
コンテキストバインドは、ビューバインドの暗黙的変換ではなく、暗黙的valueを表します。一部のタイプ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
は、_compare(a: A, b: A): Int
メソッドを定義するクラスOrdering[A]
のいずれかのタイプの暗黙的な値を取得するために使用されます。
これを行う別の方法を以下に示します。
ビューの境界とコンテキストの境界の両方が、それらの定義が与えられた暗黙的なパラメーターで実装されていることは驚くべきことではありません。実際、私が示した構文は、実際に起こることの構文糖です。砂糖をどのように脱糖するかは以下をご覧ください
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 libraryパターンを利用するために使用されます。これにより、何らかの方法で元の型を返したい状況で、既存のクラスにメソッドを「追加」します。 。何らかの方法でその型を返す必要がない場合は、ビューをバインドする必要はありません。
ビューバウンドの使用法の典型的な例は、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
に加えて、ライブラリからの最も一般的な使用法は、JavaコレクションのようにScalaクラスであるString
およびArray
を処理することです。例えば:
def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b
ビューの境界なしでこれを行おうとした場合、String
の戻り値の型はWrappedString
(Scala 2.8)になり、Array
の場合も同様です。
型が戻り型の型パラメーターとしてのみ使用される場合でも、同じことが起こります。
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted
コンテキスト境界は、主にtypeclass patternとして知られるようになったもので、Haskellの型クラスへの参照として使用されます。基本的に、このパターンは、一種の暗黙的なアダプターパターンを介して機能を使用可能にすることにより、継承に代わるものを実装します。
古典的な例は、Scala 2.8のOrdering
であり、Scalaのライブラリ全体でOrdered
を置き換えました。使用法は次のとおりです。
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の別の例はNumeric
です:
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
より複雑な例は、CanBuildFrom
の新しいコレクションの使用法ですが、それについてはすでに非常に長い答えがありますので、ここでは避けます。また、前述のように、ClassManifest
の使用法があります。これは、具体的な型なしで新しい配列を初期化するために必要です。
Typeclassパターンでバインドされたコンテキストは、関心の分離を可能にするため、独自のクラスで使用される可能性が非常に高くなりますが、ビューの境界は、適切な設計によって独自のコードで回避できます(主に他の人の設計を回避するために使用されます) )。
長い間可能でしたが、2010年にコンテキスト境界の使用が実際に開始され、Scalaの最も重要なライブラリとフレームワークのほとんどである程度使用されるようになりました。しかし、その使用法の最も極端な例は、Scalazライブラリーであり、これはHaskellの多くの能力をScalaにもたらします。タイプクラスのパターンを読んで、それを使用できるすべての方法について詳しく知ることをお勧めします。
編集
関連する質問: