283

簡単に言えば、コンテキストとビューの境界とは何ですか?また、それらの違いは何ですか?

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

4

1 に答える 1

498

これはすでに尋ねられていると思いましたが、もしそうなら、その質問は「関連」バーに表示されません。だから、ここにあります:

ビュー バウンドとは

ビュー バウンドは、Scala で導入されたメカニズムで、あるタイプをあるタイプであるかのように使用できるようにA ますB。典型的な構文は次のとおりです。

def f[A <% B](a: A) = a.bMethod

つまり、type のオブジェクトでメソッドを呼び出すことができるように、 availableAへの暗黙的な変換が必要です。標準ライブラリ (とにかく Scala 2.8.0 より前) でのビュー境界の最も一般的な使用法は、次のようなものです。BBAOrdered

def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

に変換でき、メソッドが定義AされているOrdered[A]ため、式 を使用できます。Ordered[A]<(other: A): Booleana < b

ビュー境界は非推奨であることに注意してください。避ける必要があります。

コンテキスト バウンドとは

コンテキスト境界は Scala 2.8.0 で導入され、通常、Haskell 型クラスによって提供される機能をより詳細な方法でエミュレートするコードのパターンである、いわゆる型クラス パターンと一緒に使用されます。

ビュー バウンドは単純な型 ( などA <% String) で使用できますが、コンテキスト バウンドには上記のようなパラメーター化された型が必要ですOrdered[A]、 とは異なりStringます。

コンテキスト バウンドは、ビュー バウンドの暗黙的な変換ではなく、暗黙的な値を記述します。これは、一部の typeに対して、使用可能な type の暗黙の値があることを宣言するために使用されます。構文は次のようになります。AB[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]、このクラスは method を定義します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 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て、ライブラリからの最も一般的な使用法は、Scala コレクションのような Java クラスであるStringおよびの処理です。Array例えば:

def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b

ビュー境界なしでこれを行おうとすると、 a の戻り値の型はStringa WrappedString(Scala 2.8) になり、同様にArray.

型が戻り型の型パラメーターとしてのみ使用されている場合でも、同じことが起こります。

def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted

コンテキスト境界は何に使用されますか?

コンテキスト境界は、Haskell の型クラスへの参照として、主にtypeclass patternとして知られるようになったもので使用されます。基本的に、このパターンは、一種の暗黙的なアダプター パターンを介して機能を利用できるようにすることで、継承の代替手段を実装します。

古典的な例は Scala 2.8 で、これはScala のライブラリ全体Orderingを置き換えました。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具象型なしで新しい配列を初期化するために必要な使用法があります。

型クラス パターンでバインドされたコンテキストは、関心の分離を可能にするため、独自のクラスで使用される可能性がはるかに高くなりますが、ビューの境界は、適切な設計によって独自のコードで回避できます (主に他の誰かの設計を回避するために使用されます)。 )。

長い間可能でしたが、コンテキスト境界の使用は 2010 年に実際に開始され、現在では Scala の最も重要なライブラリとフレームワークのほとんどである程度見られます。ただし、その使用法の最も極端な例は Scalaz ライブラリで、これは Haskell の多くの機能を Scala にもたらします。型クラス パターンを読んで、それを使用できるすべての方法に慣れることをお勧めします。

編集

関連する質問:

于 2010-12-17T01:43:33.003 に答える