質問
簡単に言えば、コンテキストとビューの境界とは何ですか、そしてそれらの違いは何ですか?
いくつかのわかりやすい例も素晴らしいでしょう!
解決
これはすでに尋ねられていると思いましたが、もしそうなら、「関連する」バーでは質問が明らかではありません。だから、ここにあります:
バインドされたビューとは何ですか?
a バウンドを表示します あるタイプの使用を可能にするためにScalaで導入されたメカニズムでした A
かのように それはいくつかのタイプでした 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
.
それに注意してください ビュー境界は非推奨です, 、それらを避けるべきです。
コンテキストバインドとは何ですか?
コンテキストの境界はScala 2.8.0に導入され、通常はいわゆるものとともに使用されます タイプクラスパターン, 、より冗長な方法ではあるが、Haskellタイプクラスによって提供される機能をエミュレートするコードのパターン。
ビューバウンドは、単純なタイプで使用できます(たとえば、 A <% String
)、コンテキストバインドにはaが必要です パラメーター化されたタイプ, 、 そのような Ordered[A]
上記ですが、とは異なります String
.
コンテキストバインドは、暗黙を説明しています 価値, 、View Boundの暗黙の代わりに 変換. 。何らかのタイプについて宣言するために使用されます 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)
an Array
パラメーター化されたタイプの初期化にはaが必要です ClassManifest
型の消去とアレイの非エレシュアの性質に関連する不可解な理由で、利用可能になります。
ライブラリのもう1つの非常に一般的な例は、もう少し複雑です。
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)
ここ、 implicitly
私たちが望む暗黙の値、タイプの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)
ビュー境界は何に使用されていますか?
ビュー境界は主に使用されます 私の図書館をポン引きします パターンは、既存のクラスにメソッドを「追加」し、元のタイプを何らかの形で返したい状況で「追加」します。そのタイプを決して返す必要がない場合は、バインドされたビューが必要ありません。
ビューバインドされた使用法の古典的な例は、処理です 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
, 、ライブラリからの最も一般的な使用法は処理です String
と Array
, 、それはScalaコレクションのようにJavaクラスです。例えば:
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のタイプクラスへの参照として。基本的に、このパターンは、一種の暗黙的なアダプターパターンを通じて機能を利用できるようにすることにより、継承の代替手段を実装します。
古典的な例は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の別の例は次のとおりです Numeric
:
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
より複雑な例は、の新しいコレクションの使用法です CanBuildFrom
, 、しかし、それについてはすでに非常に長い答えがあるので、ここでは避けます。そして、前に述べたように、があります ClassManifest
コンクリートタイプなしで新しい配列を初期化するために必要な使用法。
TypeClassパターンに縛られたコンテキストは、懸念の分離を可能にするため、独自のクラスで使用される可能性がはるかに高くなりますが、優れたデザインでビューの範囲を自分のコードで回避できます(他の人のデザインを回避するために主に使用されます)。
長い間可能でしたが、2010年にはコンテキスト境界の使用が実際に廃止されており、Scalaの最も重要なライブラリとフレームワークのほとんどである程度見られています。ただし、その使用の最も極端な例は、Scalazライブラリであり、Haskellの多くの力をScalaにもたらします。 Typeclassパターンを読んで、使用できるすべての方法をよりよく知ることをお勧めします。
編集
関連する興味のある質問: