[解決済み】Scalaはどこでimplicitを探すのか?
質問
について
暗黙の
Scala を初めて使う人への質問は、「コンパイラはどこでインプリシットを探すのですか?暗黙の了解というのは,その疑問が完全な形にならないからで,まるでそのための言葉がないかのようです :-) 例えば
integral
の下にあるのは、どこから来たのでしょうか?
scala> import scala.math._
import scala.math._
scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af
最初の質問に対する答えを学ぼうと決めた人へのもう一つの質問は、明らかに曖昧な状況(しかし、とにかくコンパイルする)において、コンパイラはどのように暗黙的なものを使うか選択するのだろうかというものです。
例えば
scala.Predef
からの2つの変換を定義しています。
String
への変換です。
WrappedString
と、もうひとつは
StringOps
. しかし,どちらのクラスも多くのメソッドを共有しています.
map
?
注 この質問は この他の質問 この問題は、より一般的な方法で説明することを期待しています。例題は、解答の中で参照されているため、そこからコピーしました。
どのように解決するのですか?
インプリシットの種類
Scalaにおけるインプリシットは、いわば自動的に渡される値、あるいは自動的に行われるある型から別の型への変換のことを指します。
暗黙の変換
後者のタイプについてごく簡単に説明すると、あるメソッドを呼び出すと、そのメソッドは
m
オブジェクトの
o
あるクラスの
C
をサポートせず、そのクラスがメソッド
m
からの暗黙の変換を探します。
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
から
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
).
コンテキスト・バウンド
暗黙のパラメータでもう一つよくあるパターンは 型クラスパターン . このパターンは、共通のインターフェイスを宣言していないクラスに対して、共通のインターフェイスを提供することを可能にします。ブリッジ・パターン(関心事の分離)としても、アダプター・パターンとしても機能します。
この
Integral
クラスは、型クラスパターンの典型的な例です。Scalaの標準ライブラリにあるもう一つの例は
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
について
Seq
には暗黙の
Ordering
. メソッドを作成するには
reverseSort
と書くことができる。
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
なぜなら
Ordering[T]
に暗黙のうちに渡されました。
reverseSort
に暗黙のうちに渡すことができます。
sorted
.
インプリシットはどこから来たのか?
オブジェクトのクラスに存在しないメソッドを呼び出したり、暗黙のパラメータを必要とするメソッドを呼び出したりして、コンパイラが暗黙の必要性を認識すると、その必要性に適合する暗黙を検索します。
この検索は、どのインプリシットが表示され、どのインプリシットが表示されないかを定義する一定の規則に従います。次の表は、コンパイラがインプリシットを検索する場所を示すものです。 プレゼンテーション Josh Suerethによるimplicitsについての記事で、Scalaの知識を高めたい人には心からお勧めします。その後、フィードバックやアップデートによって補完されています。
以下の1番で利用できるインプリシットは、2番のものよりも優先されます。それ以外では、暗黙のパラメータの型にマッチする引数が複数ある場合、静的オーバーロードの解決ルール(Scala Specification §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つのオブジェクトコンパニオンです。まず、"source"タイプのオブジェクトコンパニオンが調べられます。例えば、オブジェクトの内部
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
.
引数の型の暗黙のスコープ
引数型のメソッドがある場合
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
そして、探すべき "source" 型もありません。明らかに、それは
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"
その他の寸法
これは冗談だと思いますが、この回答は最新でない可能性があります。ですから、この質問が最終的な判断材料になるとは思わないでください。もし、古くなっていることに気づいたら、修正しますので、お知らせください。
EDIT
気になる関連質問
関連
-
[解決済み] Scala: スライド(N,N) vs グループ化(N)
-
[解決済み] データフレームを複数回グループ化する
-
[解決済み] TimeoutExceptionが発生した場合、どのような原因が考えられるでしょうか。Sparkで作業しているときに[n秒]後にFuturesがタイムアウトしました[重複]。
-
[解決済み] MapのmapValuesとtransformの違いについて
-
[解決済み】Scalaの記号演算子にはどんな意味があるの?
-
[解決済み】タイプセーフのenum型をモデル化する方法は?
-
[解決済み】case objectとobjectの違いについて
-
[解決済み】Scalaのコンテキストとビューバウンドとは何ですか?
-
[解決済み】レイジーバルって何するもの?
-
[解決済み] Spark SQLでカラムの降順でソートするには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Sparkです。CoarseGrainedSchedulerが見つかりませんでした。
-
[解決済み] Scala subString関数
-
[解決済み] TimeoutExceptionが発生した場合、どのような原因が考えられるでしょうか。Sparkで作業しているときに[n秒]後にFuturesがタイムアウトしました[重複]。
-
[解決済み] Scalaの''って何?
-
[解決済み】Akka Kill vs. Stop vs. Poison Pill?
-
[解決済み】ScalaでVectorを選択するのはどんなとき?
-
[解決済み】Scalaのコンテキストとビューバウンドとは何ですか?
-
[解決済み] Scalaの識別子 "implicitly "とは?
-
[解決済み] Build.scala、%および%%の記号の意味
-
[解決済み] Scalaのパターンマッチングシステムで比較演算子を使う