web-dev-qa-db-ja.com

Scalaコンテキストおよびビュー境界とは何ですか?

簡単な方法で、コンテキストとビューの境界とは何ですか?それらの違いは何ですか?

いくつかのわかりやすい例も素晴らしいでしょう!

258
chrsan

私はこれはすでに尋ねられたと思ったが、もしそうなら、質問は「関連する」バーで明白ではない。だから、ここにあります:

ビューバウンドとは

ビューバウンドは、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

AOrdered[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の処理です。暗黙的な変換がありますが、IntOrderedではないことに注意してください。前述の例では、変換されていない型を返すため、ビューをバインドする必要があります。

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

Context Boundsは何に使用されますか?

コンテキスト境界は、主に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にもたらします。タイプクラスのパターンを読んで、それを使用できるすべての方法について詳しく知ることをお勧めします。

編集

関連する質問:

463