簡単に言えば、コンテキストとビューの境界とは何ですか?また、それらの違いは何ですか?
いくつかのわかりやすい例も素晴らしいでしょう!
これはすでに尋ねられていると思いましたが、もしそうなら、その質問は「関連」バーに表示されません。だから、ここにあります:
ビュー バウンドは、Scala で導入されたメカニズムで、あるタイプをあるタイプであるかのように使用できるようにA
しますB
。典型的な構文は次のとおりです。
def f[A <% B](a: A) = a.bMethod
つまり、type のオブジェクトでメソッドを呼び出すことができるように、 availableA
への暗黙的な変換が必要です。標準ライブラリ (とにかく Scala 2.8.0 より前) でのビュー境界の最も一般的な使用法は、次のようなものです。B
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
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]
、このクラスは 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 の戻り値の型はString
a 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 にもたらします。型クラス パターンを読んで、それを使用できるすべての方法に慣れることをお勧めします。
編集
関連する質問: