implicit parameter(暗黙のパラメータ)


implicit parameterは主として2つの目的で使われます。1つ目の目的は、あちこちのメソッドで共通で引き渡されるオブジェクト(たとえば、ソケットやデータベースのコネクションなど)を明示的に引き渡すのを省略するために使うものです。
以下に定義を示します。

def userDatabase(...)(implicit conn: Connection)

implicit修飾子は引数の先頭の要素に付けなければならないという制約があり、implicitを使うにはカリー化されたメソッド定義が必要になります。

もう一つの使い方は少々変わっています。まず、Listの全ての要素の値を加算した結果を返すsumメソッドを定義したいと思います。
ポイントは何のListかわからない点にあります。
整数の+メソッドをそのまま使ったりはできないということです。このような場合、2つの手順を踏みます。
まず、2つの同じ型を足す方法を知っている型を定義します。

trait Additive[A] {
  def plus(a: A, b: A): A
  def zero: A
}
// ここで、Additiveの型パラメータはListの要素の型を表しています。
// 次にこのAdditive型を使って、Listの全ての要素を合計するメソッドを定義します。
def sum[A](lst: List[A])(m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y))
// 後は、それぞれの型に応じた加算と0の定義を持ったobjectを定義します。
object StringAdditive extends Additive[String] {
  def plus(a: String, b: String): String = a + b
  def zero: String = ""
}
object IntAdditive extends Additive[Int] {
  def plus(a: Int, b: Int):Int = a + b
  def zero: Int = 0
}
println(sum(List(1, 2, 3))(IntAdditive))
println(sum(List("A", "B", "C"))(StringAdditive))

とすれば良いだけです。
しかし、何のListの要素を合計するかは型チェックする時点でわかっているのだからいちいちIntAdditive、StringAdditiveを明示的に渡さなくとも賢く推論してほしいものです。
そして、それを実現するのがimplicit parameterです。
方法はStringAdditiveとIntAdditiveの定義の前にimplicitを付けることと、sumの最後の引数リストのmにimplicitを付けるだけです。

trait Additive[A] {
  def plus(a: A, b: A): A
  def zero: A
}
def sum[A](lst: List[A])(implicit m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y))
implicit object StringAdditive extends Additive[String] {
  def plus(a: String, b: String): String = a + b
  def zero: String = ""
}
implicit object IntAdditive extends Additive[Int] {
  def plus(a: Int, b: Int):Int = a + b
  def zero: Int = 0
}
println(sum(List(1, 2, 3)))
println(sum(List("A", "B", "C")))

implict defやimplicit parameterの値の探索範囲には

  • ローカルで定義されたもの
  • importで指定されたもの
  • スーパークラスで定義されたもの
  • コンパニオンオブジェクトで定義されたもの

などがあります。