Scala 2.8の新機能の1つはコンテキスト境界です。コンテキスト境界とは何ですか?
もちろん最初に検索しました(そして、たとえば this を見つけました)が、本当に明確で詳細な情報を見つけることができませんでした。
この記事 を見つけましたか?配列の改善のコンテキスト内で、新しいコンテキストバウンド機能について説明します。
一般に、context boundを持つ型パラメーターは[T: Bound]
;という形式です。タイプBound[T]
の暗黙的なパラメーターと共に、プレーンなタイプパラメーターT
に展開されます。
0から特定の長さまでの数値の範囲に特定の関数fを適用した結果から配列を形成するメソッドtabulate
を考えます。 Scala 2.7まで、表は次のように記述できます。
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]
を渡すことでこの情報を提供する必要があります暗黙的なパラメーターとしてのメソッド:
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
}
短縮形として、代わりにcontext boundを型パラメーター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
}
Robertの答えは、Context Boundsの技術的な詳細をカバーしています。それらの意味についての私の解釈をお伝えします。
In Scala aビュー境界(A <% B
)「次のように見える」という概念をキャプチャします(上限<:
は「is a」の概念をキャプチャします)。コンテキストバウンド(A : C
)型について「ある」と言います。 「T
にはManifest
があります」とマニフェストに関する例を読むことができます。 Ordered
vs Ordering
にリンクした例は、違いを示しています。方法
def example[T <% Ordered[T]](param: T)
パラメータはOrdered
として見ることができると言います。と比べて
def example[T : Ordering](param: T)
これは、パラメーターにOrdering
が関連付けられていることを示しています。
使用に関しては、規約が確立されるまで少し時間がかかりましたが、コンテキスト境界はビュー境界よりも優先されます( ビュー境界は非推奨になりました )。暗示的な定義をあるスコープから別のスコープに直接参照する必要なしに転送する必要がある場合、コンテキストバインドが推奨されます(配列の作成に使用されるClassManifest
の場合は確かです)。
ビューの境界とコンテキストの境界についてのもう1つの考え方は、最初の呼び出しが呼び出し元のスコープから暗黙的な変換を転送することです。 2番目は、呼び出し元のスコープから暗黙的なオブジェクトを転送します。
(これは括弧書きです。最初に他の回答を読んで理解してください。)
コンテキスト境界は、実際にはビュー境界を一般化します。
したがって、次のコードは、ビューバウンドで表されます。
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
これは、Context Boundで、タイプF
からタイプ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
コンテキストバインドは、種類* => *
の型コンストラクタと共に使用する必要があります。ただし、型コンストラクタFunction1
は種類(*, *) => *
です。型エイリアスの使用は、型String
を持つ2番目の型パラメーターを部分的に適用し、コンテキストバウンドとして使用するための正しい種類の型コンストラクターを生成します。
特性内で型エイリアスを使用せずに、Scalaで部分的に適用された型を直接表現できるようにする提案があります。次に書くことができます:
def f3[T : [X](X => String)](t: T) = 0
これは別の括弧書きです。
Benが指摘した のように、コンテキストバインドは、型パラメーターと型クラスの間の "has-a"制約を表します。言い換えると、特定の型クラスの暗黙的な値が存在するという制約を表します。
コンテキストバインドを利用する場合、多くの場合、その暗黙の値を明らかにする必要があります。たとえば、制約T : Ordering
が与えられると、多くの場合、制約を満たす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) }