Scala 2.8 の新機能の 1 つにコンテキスト境界があります。コンテキスト バウンドとは何ですか? また、どこで役立ちますか?
もちろん、最初に検索しました(たとえばこれを見つけました)が、本当に明確で詳細な情報を見つけることができませんでした。
Scala 2.8 の新機能の 1 つにコンテキスト境界があります。コンテキスト バウンドとは何ですか? また、どこで役立ちますか?
もちろん、最初に検索しました(たとえばこれを見つけました)が、本当に明確で詳細な情報を見つけることができませんでした。
ロバートの答えは、コンテキスト境界の技術的な詳細をカバーしています。それらの意味の私の解釈をお伝えします。
Scala では、View Bound ( A <% B
) は「次のように見える」という概念を捉えます (一方、上限<:
は「ある」という概念を捉えます)。コンテキスト バウンド ( A : C
) は、型について「has a」を示します。マニフェストに関する例は、「T
has a Manifest
」として読むことができます。Ordered
about vsにリンクした例Ordering
は、違いを示しています。方法
def example[T <% Ordered[T]](param: T)
は、パラメータが として見ることができると言いますOrdered
。と比べて
def example[T : Ordering](param: T)
これは、パラメータに が関連付けられていることを示していますOrdering
。
使用に関しては、規則が確立されるまでに時間がかかりましたが、ビューの境界よりもコンテキストの境界が優先されます (ビューの境界は非推奨になりました)。1 つの提案は、暗黙的な定義をあるスコープから別のスコープに直接参照する必要なく転送する必要がある場合は、コンテキスト バウンドが優先されることです (これは ClassManifest
、配列の作成に使用される の場合に当てはまります)。
ビューの境界とコンテキストの境界について考える別の方法は、最初に呼び出し元のスコープから暗黙的な変換を転送することです。2 番目は、暗黙的なオブジェクトを呼び出し元のスコープから転送します。
この記事は見つかりましたか?配列の改善のコンテキスト内で、新しいコンテキスト バインド機能をカバーします。
一般に、コンテキストがバインドされた型パラメーターの形式は[T: Bound]
; T
typeの暗黙的なパラメーターとともに、プレーンな型パラメーターに展開されますBound[T]
。
tabulate
与えられた関数 f を 0 から与えられた長さまでの数値の範囲に適用した結果から配列を形成するメソッドを考えてみましょう。Scala 2.7 までは、tabulate は次のように記述できました。
def tabulate[T](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Scala 2.8 では、 の正しい表現を作成するために実行時情報が必要になるため、これはもはや不可能ですArray[T]
。ClassManifest[T]
メソッドに a を暗黙のパラメーターとして渡すことで、この情報を提供する必要があります。
def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
省略形として、代わりにコンテキスト バウンドを型パラメーターで使用できます。T
def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
(これは括弧書きです。最初に他の回答を読んで理解してください。)
コンテキスト境界は、実際にはビュー境界を一般化します。
したがって、ビュー バウンドで表現されたこのコードを考えると、次のようになります。
scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String
scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int
F
これは、 type から typeへの関数を表す型エイリアスの助けを借りて、Context Bound で表現することもできますT
。
scala> trait To[T] { type From[F] = F => T }
defined trait To
scala> def f2[T : To[String]#From](t: T) = 0
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int
scala> f2(1)
res1: Int = 0
コンテキスト バインドは、 kind の型コンストラクタで使用する必要があります* => *
。ただし、型コンストラクターFunction1
は kind(*, *) => *
です。型エイリアスを使用すると、2 番目の型パラメーターが type に部分的に適用さString
れ、コンテキスト バインドとして使用するための正しい種類の型コンストラクターが生成されます。
トレイト内で型エイリアスを使用せずに、Scala で部分的に適用された型を直接表現できるようにする提案があります。次に、次のように記述できます。
def f3[T : [X](X => String)](t: T) = 0
これは別の括弧内のメモです。
Ben が指摘したように、コンテキスト バウンドは、型パラメーターと型クラスの間の "has-a" 制約を表します。別の言い方をすれば、特定の型クラスの暗黙的な値が存在するという制約を表します。
コンテキスト バウンドを利用する場合、多くの場合、その暗黙的な値を明らかにする必要があります。たとえば、constraint が与えられた場合、多くの場合、constraint を満たす の T : Ordering
インスタンスが必要になります。here で示されているように、メソッドまたはもう少し便利なメソッドを使用して、暗黙的な値にアクセスできます。Ordering[T]
implicitly
context
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }
また
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => context[T]().times(t._1, t._2) }