暗黙のタイプ
Scalaの暗黙的表現は、いわば「自動的に」渡すことができる値、または自動的に行われるあるタイプから別のタイプへの変換を指します。
暗黙的な変換
1のメソッドを呼び出す場合、後者のタイプについては非常に簡単に言えば、m
オブジェクト上o
のクラスのC
、そのクラスはメソッドをサポートしていないm
場合、Scalaはからの暗黙的な変換を探しますC
何かにない支援をm
。簡単な例では、方法になりますmap
上String
:
"abc".map(_.toInt)
String
メソッドをサポートしていませんmap
が、StringOps
ない、との暗黙的な変換がありますString
にStringOps
(参照可能なのimplicit def augmentString
でPredef
)。
暗黙的なパラメーター
他の種類の暗黙的なものは、暗黙的なパラメータです。これらは他のパラメーターと同様にメソッド呼び出しに渡されますが、コンパイラーはそれらを自動的に埋めようとします。それができない場合は、文句を言うでしょう。一つは、することができますどのようにいずれかを使用している、明示的にこれらのパラメータを渡すbreakOut
例えば、(についての質問を参照してくださいbreakOut
あなたが挑戦のためにアップを感じている日に、)。
この場合、foo
メソッド宣言などの暗黙の必要性を宣言する必要があります。
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
境界を表示
暗黙的なものが暗黙的な変換と暗黙的なパラメーターの両方である状況が1つあります。例えば:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
getIndex
クラスからへの暗黙の変換が利用できる限り、メソッドは任意のオブジェクトを受け取ることができますSeq[T]
。そのため、String
to を渡すことができgetIndex
、それは機能します。
舞台裏では、コンパイラが変更seq.IndexOf(value)
にconv(seq).indexOf(value)
。
これは非常に便利なので、それらを記述する構文糖があります。この構文糖を使用getIndex
すると、次のように定義できます。
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
この糖衣構文は次のように記載されて結合されたビューに類似し、上限(CC <: Seq[Int]
)または下限(T >: Null
)。
コンテキスト境界
暗黙的なパラメーターのもう1つの一般的なパターンは、型クラスパターンです。このパターンにより、クラスを宣言していないクラスへの共通インターフェースを提供できます。これは、ブリッジパターン(懸念を分離する)としても、アダプターパターンとしても機能します。
Integral
あなたが言及したクラスには、型クラスのパターンの典型的な例です。Scalaの標準ライブラリのもう1つの例はOrdering
です。Scalazと呼ばれる、このパターンを多用するライブラリーがあります。
これはその使用例です:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
コンテキストバウンドと呼ばれるそのためのシンタックスシュガーもあり、暗黙的に参照する必要があるため、あまり有用ではありません。そのメソッドを直接変換すると、次のようになります。
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
コンテキストの境界は、それを使用する他のメソッドに渡す必要がある場合に、より便利です。たとえば、メソッドsorted
on Seq
には暗黙のが必要Ordering
です。メソッドを作成するには、次のreverseSort
ように記述します。
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
のでOrdering[T]
、暗黙的に渡されたreverseSort
、それはその後に暗黙のうちにそれを渡すことができますsorted
。
インプリシットはどこから来るのですか?
オブジェクトのクラスに存在しないメソッドを呼び出しているため、または暗黙のパラメーターを必要とするメソッドを呼び出しているために、コンパイラーが暗黙の必要性を認識すると、ニーズに適合する暗黙を検索します。
この検索は、どのインプリシットが表示され、どのインプリシットが表示されないかを定義する特定のルールに従います。コンパイラーがインプリシットを検索する場所を示す次の表は、Josh Suerethによるインプリシットに関する優れたプレゼンテーションから引用したものです。それ以来、フィードバックとアップデートで補完されています。
以下の番号1で使用できる暗黙的要素は、番号2の下で使用可能な暗黙的要素よりも優先されます。それ以外に、暗黙的パラメーターの型に一致する適格な引数がいくつかある場合、静的オーバーロード解決の規則を使用して最も具体的な引数が選択されます(Scalaを参照)仕様§6.26.3)。より詳細な情報は、この回答の最後にリンクする質問にあります。
- 現在のスコープを最初に見る
- 現在のスコープで定義されている暗黙的表現
- 明示的なインポート
- ワイルドカードのインポート
他のファイルでも同じスコープ
- 次に、関連する型を見てください
- タイプのコンパニオンオブジェクト
- 引数の型の暗黙のスコープ(2.9.1)
- 型引数の暗黙のスコープ(2.8.0)
- ネストされたタイプの外部オブジェクト
- その他の寸法
それらの例をいくつか挙げましょう。
現在のスコープで定義されている暗黙的表現
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
明示的なインポート
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
ワイルドカードのインポート
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
他のファイルでも同じスコープ
編集:これは別の優先順位を持たないようです。優先順位の違いを示す例がある場合は、コメントしてください。それ以外の場合は、これに依存しないでください。
これは最初の例と似ていますが、暗黙の定義がその使用方法とは異なるファイルにあると想定しています。暗黙的に導入するためにパッケージオブジェクトがどのように使用されるかも参照してください。
タイプのコンパニオンオブジェクト
ここには、注目すべき2つのオブジェクトコンパニオンがあります。まず、「ソース」タイプのオブジェクトコンパニオンが調べられます。たとえば、オブジェクトの内部Option
ではへの暗黙の変換があるためIterable
、のIterable
メソッドを呼び出すか、を期待するものにOption
渡すことができます。例えば:Option
Iterable
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield (x, y)
その式はコンパイラによって次のように変換されます
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
しかし、List.flatMap
期待しTraversableOnce
ている、Option
ではありません。次に、コンパイラはOption
のオブジェクトコンパニオンを調べて、への変換を見つけます。これIterable
はでありTraversableOnce
、この式を正しくします。
次に、予期されるタイプのコンパニオンオブジェクト:
List(1, 2, 3).sorted
このメソッドsorted
は暗黙的Ordering
です。この場合、オブジェクトの内部Ordering
、クラスのコンパニオンを調べOrdering
、Ordering[Int]
そこで暗黙を見つけます。
スーパークラスのコンパニオンオブジェクトも調べられることに注意してください。例えば:
class A(val n: Int)
object A {
implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b // s == "A: 2"
これはScalaが暗黙のうちNumeric[Int]
にNumeric[Long]
、そしてあなたの質問の中で、ところで、内Numeric
ではなく、で発見された方法Integral
です。
引数の型の暗黙のスコープ
引数typeを持つメソッドがある場合、type A
の暗黙のスコープA
も考慮されます。「暗黙のスコープ」とは、これらのすべてのルールが再帰的に適用されることを意味します。たとえば、上記のルールに従って、コンパニオンオブジェクトのA
暗黙オブジェクトが検索されます。
これはA
、そのパラメータの変換ではなく、式全体の暗黙的なスコープが検索されることを意味するわけではないことに注意してください。例えば:
class A(val n: Int) {
def +(other: A) = new A(n + other.n)
}
object A {
implicit def fromInt(n: Int) = new A(n)
}
// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)
これはScala 2.9.1以降で利用可能です。
型引数の暗黙のスコープ
これは、型クラスパターンを実際に機能させるために必要です。Ordering
たとえば、を考えてみてください。コンパニオンオブジェクトにはいくつかの暗黙的要素が含まれていますが、要素を追加することはできません。それでは、どのようにして、Ordering
自動的に検出される独自のクラスを作成できますか?
実装から始めましょう:
class A(val n: Int)
object A {
implicit val ord = new Ordering[A] {
def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
}
}
だから、あなたが電話したときに何が起こるかを考えてください
List(new A(5), new A(2)).sorted
私達が見たように、この方法は、sorted
期待しOrdering[A]
(実際には、それは期待しOrdering[B]
、どこB >: A
)。内部にはそのようなものOrdering
はなく、調べる「ソース」タイプもありません。明らかに、それはA
の型引数であるの中にありOrdering
ます。
これは、さまざまなコレクションメソッドが機能することを期待する方法でもありますCanBuildFrom
。暗黙的なものは、の型パラメーターのコンパニオンオブジェクト内にありCanBuildFrom
ます。
注:Ordering
のように定義されるtrait Ordering[T]
場合、T
型パラメータです。以前は、Scalaは型パラメーターの内部を見ていると言っていましたが、あまり意味がありません。上記探し暗黙的であるOrdering[A]
場合、A
パラメータを入力しないで、実際の型である:それは型引数にOrdering
。Scala仕様のセクション7.2を参照してください。
これはScala 2.8.0以降で利用可能です。
ネストされたタイプの外部オブジェクト
これの例は実際には見ていません。誰かが共有してくれるとありがたいです。原理は簡単です:
class A(val n: Int) {
class B(val m: Int) { require(m < n) }
}
object A {
implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b // s == "B: 3"
その他の寸法
これは冗談だったと思いますが、この答えは最新のものではないかもしれません。ですから、この質問を何が起こっているかの最終的な判断者だと考えないでください。古くなっていることに気付いた場合は、私が修正できるようにお知らせください。
編集
関心のある関連質問: