メソッドは 暗黙の パラメータのリストを持つことができ、パラメータリストの先頭には implicit キーワードで印をつけます。 もしそのパラメータリストの中のパラメータがいつものように渡らなければ、Scalaは正しい型の暗黙値を受け取ることができるかを確認し、可能であればそれを自動的に渡します。
Scalaがこれらのパラメータを探す場所は2つのカテゴリに分かれます。
- Scalaはまず最初に暗黙のパラメータブロックを持つメソッドが呼び出されている箇所で、直接(プレフィックスなしに)アクセスできる暗黙の定義と暗黙のパラメータを探します。
- 次に、候補となる型に関連づけられた全てのコンパニオンオブジェクトの中でimplicitと宣言されているメンバーを探します。
Scalaがimplicitをどこから見つけるかについてのより詳しいガイドはFAQで見ることができます。
以下の例では、モノイドのadd
とunit
の演算を使い、要素のリストの合計を計算するメソッドsum
を定義しています。
implicitの値をトップレベルには置けないことに注意してください。
abstract class Monoid[A] {
def add(x: A, y: A): A
def unit: A
}
object ImplicitTest {
implicit val stringMonoid: Monoid[String] = new Monoid[String] {
def add(x: String, y: String): String = x concat y
def unit: String = ""
}
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
def add(x: Int, y: Int): Int = x + y
def unit: Int = 0
}
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if (xs.isEmpty) m.unit
else m.add(xs.head, sum(xs.tail))
def main(args: Array[String]): Unit = {
println(sum(List(1, 2, 3))) // intMonoidを暗に使用
println(sum(List("a", "b", "c"))) // stringMonoidを暗に使用
}
}
Monoid
はここではadd
と呼ばれる処理を定義します。この処理はA
のペアを結合して、別のA
を返します。また、(特定の)A
を作ることができるunit
と呼ばれる処理も定義します。
暗黙のパラメータがどのように働くかを見るため、まずは文字列と整数のためにそれぞれモノイドstringMonoid
とintMonoid
を定義します。implicit
キーワードは対応するオブジェクトが暗黙に使われうることを指し示します。
メソッドsum
はList[A]
を受け取り、A
を返します。このメソッドは初期値A
をunit
から受け取り、リスト中のA
を順番にadd
メソッドで結合します。ここでパラメータm
をimplicitにしているのは、そのメソッドを呼び出すとき、Scalaが暗黙のパラメータm
として暗黙のMonoid[A]
を見つけることができるなら、私達はxs
パラメータを提供するだけで良いということです。
main
メソッドではsum
を2回呼んでいて、xs
パラメータだけを渡しています。するとScalaは先に言及したスコープの中でimplicitを探します。最初のsum
の呼び出しはxs
としてList[Int]
を渡します。それは A
がInt
であることを意味します。暗黙のパラメータリストm
が省略されているので、Scalaは暗黙のMonoid[Int]
を探します。最初の探索ルールはこうでした。
Scalaはまず最初に暗黙のパラメータブロックを持つメソッドが呼び出されている箇所で、直接(プレフィックスなしに)アクセスできる暗黙の定義と暗黙のパラメータを探します。
intMonoid
はmain
の中で直接アクセスできる暗黙の定義です。型も一致しているので、sum
メソッドに自動的に渡されます。
sum
の2回目の呼び出しはList[String]
を渡します。それはA
はString
であることを意味します。暗黙の値の探索はInt
の時と同様に動きますが、今回は stringMonoid
を見つけ、m
として自動的に渡します。
そのプログラムは以下を出力します。
6
abc