web-dev-qa-db-ja.com

Scalaの「コンテキストバウンド」とは何ですか?

Scala 2.8の新機能の1つはコンテキスト境界です。コンテキスト境界とは何ですか?

もちろん最初に検索しました(そして、たとえば this を見つけました)が、本当に明確で詳細な情報を見つけることができませんでした。

106
Jesper

この記事 を見つけましたか?配列の改善のコンテキスト内で、新しいコンテキストバウンド機能について説明します。

一般に、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
}
100
Robert Harvey

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番目は、呼び出し元のスコープから暗黙的なオブジェクトを転送します。

138
Ben Lings

(これは括弧書きです。最初に他の回答を読んで理解してください。)

コンテキスト境界は、実際にはビュー境界を一般化します。

したがって、次のコードは、ビューバウンドで表されます。

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 
38
retronym

これは別の括弧書きです。

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) }
17
Aaron Novstrup