あるプログラマの日記

プログラマのメモ、出来事、考えたこと、勉強とかの雑記

暗黙の引数 (implicit parameters)

引数の型に合わせた暗黙の値を指定しておくと、関数(メソッド)呼び出し時の引数を省略できる。
関数定義の引数リストで引数名の前に implicit を付けると暗黙の引数が適用される。
引数を省略しないときは関数呼び出し時の指定値がそのまま渡される。
implicit は引数リスト内の先頭の引数以外には指定できないが、これは個別の引数への指定ではなく
引数リスト全体に適用されている。
デフォルト引数によく似ているが、デフォルト引数は、関数の定義でデフォルト値を指定する
のに対して暗黙の引数は呼び出し元で引き数の暗黙の値を定義する。
例では、一般的な Int と Long にマッチさせる暗黙の引数を定義しているが、通常は個別に型を
作って偶然の型の一致が起こらないようにするのが良さそうである。

scala> def foo(a: Int, implicit b: Long): Long = a * b
<console>:1: error: identifier expected but 'implicit' found.
       def foo(a: Int, implicit b: Int) = a * b
                       ^
scala> def foo(implicit a: Int, b: Long): Long = a * b
foo: (implicit a: Int, implicit b: Long)Long

scala> implicit val defValue = 10
defValue: Int = 10

scala> implicit val defLValue = 20L
defLValue: Long = 20

scala> foo
res1: Long = 200

scala> foo(3, 4)
res2: Long = 12

複数の引数リストをとる関数の最後尾の引数リスト以外に暗黙の引数は適用できない。

scala> def bar(a: Short)(implicit b: Long, c: Int): Long = a + b * c
bar: (a: Short)(implicit b: Long, implicit c: Int)Long

scala> bar(5)
res3: Long = 205

scala> def foo2(implicit a: Int)(b: Int): Int = a * b
<console>:1: error: '=' expected but '(' found.
       def foo2(implicit a: Int)(b: Int): Int = a * b
                                ^

scala> def foo3(a: Int)(implicit b: Long)(c: Short): Long = a * b + c
<console>:1: error: '=' expected but '(' found.
       def foo3(a: Int)(implicit b: Long)(c: Short): Long = a * b + c
                                         ^

scala> def foo4(implicit a: Int)(implicit b: Long): Long = a * b
<console>:1: error: '=' expected but '(' found.
       def foo4(implicit a: Int)(implicit b: Long): Long = a * b
                                ^

呼び出した関数で適用した暗黙の引数の型の implicit val 定義が
スコープ内になければエラーになる。

scala> def bar2(a: Int)(implicit b: Long, c: Short): Long = a + b * c
bar7: (a: Int)(implicit b: Long, implicit c: Short)Long

scala> bar2(7)
<console>:11: error: could not find implicit value for parameter c: Short
       bar2(7)

scala> implicit val defSValue: Short = 5
defSValue: Short = 5

scala> bar2(7)
res8: Long = 107

呼び出した関数で適用した暗黙の引数の型のimplicit val の定義がスコープ内で
2つ以上ある場合もエラーになる。

scala> implicit val defs2: Short = 3
defs2: Short = 3

scala> bar2(7)
<console>:13: error: ambiguous implicit values:
 both value defSValue in object $iw of type => Short
 and value defs2 in object $iw of type => Short
 match expected type Short
       bar2(7)
           ^

あるスコープで、型に定義している暗黙の値を確認するには Predef の implicitly[型] メソッドを使う。

scala> implicitly[Int]
res10: Int = 10

scala> implicitly[Long]
res11: Long = 20

Predef の implicitly の 定義

def implicitly[T](implicit e: T): T = e