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


115

Scala 2.8の新機能の1つはコンテキスト境界です。コンテキストバウンドとは何ですか?どこで役立ちますか?

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


8
また、境界のすべての種類のツアーのためにこれをチェックアウト:gist.github.com/257758/47f06f2f3ca47702b3a86c76a5479d096cb8c7ec
Arjan Blokzijlの

2
:この優れた答えは/コントラストコンテキスト境界とビューの境界比較stackoverflow.com/questions/4465948/...
アーロンNovstrup

これは非常に素晴らしい回答ですstackoverflow.com/a/25250693/1586965
samthebest

回答:


107

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

一般に、コンテキストがバインドされた型パラメーターはの形式[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
}

145

Robertの答えは、Context Boundsの技術的な詳細をカバーしています。私はそれらの意味の私の解釈を提供します。

Scalaでは、View Bound(A <% B)は「次のように見える」という概念をキャプチャします(一方、上限<:は「is a」の概念をキャプチャします)。コンテキストバウンド(A : C)は、型について「ある」を示します。マニフェストの例Tは、Manifest「がある」として読むことができます。about Orderedvsにリンクした例Orderingは、違いを示しています。方法

def example[T <% Ordered[T]](param: T)

パラメータはと見なすことができると言いますOrdered。と比べて

def example[T : Ordering](param: T)

これは、パラメータが関連付けられていることを示していますOrdering

使用の面では、規約が確立されるまでにしばらく時間がかかりましたが、ビューの境界よりもコンテキストの境界が推奨されます(ビューの境界は非推奨になりました)。1つの提案は、暗黙的な定義をあるスコープから別のスコープに直接参照する必要なく転送する必要がある場合に、コンテキストバインドが推奨されることです(これはClassManifest、配列を作成するためにが使用されている場合に当てはまり ます)。

ビューの境界とコンテキストの境界について考えるもう1つの方法は、最初の呼び出しが呼び出し元のスコープから暗黙の変換を転送することです。2番目は、呼び出し元のスコープから暗黙的なオブジェクトを転送します。


2
「is a」または「seen as」ではなく「has a」が私にとって重要な洞察でした-他の説明ではこれは見られませんでした。それ以外はわずかに不可解な演算子/関数のプレーンな英語バージョンがあると、吸収がはるかに簡単になります-ありがとう!
DNA

1
@Ben Lingsどういう意味ですか.... タイプについて「持っている」 ...?タイプとは何ですか?
jhegedus 14年

1
@jhegedusこれが私の構文解析です。「型について」は、Aが型を参照することを意味します。「has a」という語句は、オブジェクト指向の設計でオブジェクトの関係を表すためによく使用されます(たとえば、顧客は「has an」アドレスを持っています)。しかし、ここでの「has a」関係は、オブジェクト間ではなく、タイプ間の関係です。「has a」の関係は、OO設計のように固有または普遍的ではないため、大まかに言えば類推です。カスタマーは常にアドレスを持っていますが、コンテキストバインドの場合、Aは常にCを持っているわけではありません。むしろ、コンテキストバインドは、C [A]のインスタンスを暗黙的に提供する必要があることを指定します。
jbyler

私は1か月間Scalaを学んでおり、これが今月見た中で最高の説明です。@ベンありがとうございます!
Lifu Huang

@Ben Lings:ありがとう、コンテキストバインドとは何かを理解するために長い時間を費やした後、あなたの答えは非常に役に立ちます。[ has a私にはより理にかなっています]
Shankar

39

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

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

したがって、ビューバウンドで表現されたこのコードを考えると:

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 

f2の定義における#Fromの意味を説明できますか?タイプFがどこで構築されるのかわかりません(これは正しく言っていましたか?)
Collin

1
これは、型プロジェクションと呼ばれ、型の型メンバーFromを参照しTo[String]ます。には型引数を提供しないため、型ではなくFrom型コンストラクタを参照します。この型コンストラクタは、コンテキストバインドとして使用するのに適した種類* -> *です- 。これは、型Tの暗黙的なパラメータを要求することにより、型パラメータを制限しますTo[String]#From[T]。タイプエイリアスを展開すると、できあがりですFunction1[String, T]
レトロネーム2010年

それはFunction1 [T、String]である必要がありますか?
ssanj、2011年

18

これは別の括弧付きのメモです。

ベンは指摘し、バインドコンテキストは「持っている、」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) }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.