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