Implicit Parameter

Implicit Parameter は暗黙の引数だ。2つの使い方があるようだけど、今日はその1つ、あちこちで共通に使われる引数を、いちいち明示的に渡すのを省略するための使い方を見ていこう。

参考にしている Dwango の研修資料では、データベースのコネクションの例が示されているけど、ここではもっと簡単な例を示す。

scala> def add(x: Int)(implicit y: Int): Int = x + y
add: (x: Int)(implicit y: Int)Int

scala> def sub(x: Int)(implicit y: Int): Int = x - y
sub: (x: Int)(implicit y: Int)Int

scala> def mul(x: Int)(implicit y: Int): Int = x * y
mul: (x: Int)(implicit y: Int)Int

Implicit Parameter を使うには、引数宣言に implicit キーワードをつけるだけだ。ただし、implicit キーワードをつけられるのは引数リストの最初に引数だけ、という制約があるので、通常は複数の引数リストを持つメソッドにして最後に Implicit Parameter を持ってくるようだ。

さて、これらのメソッドを呼び出すと、Scala は現在のスコープで直近にある implicit とマークされた変数の値を、Implicit Parameter としてメソッドに引き渡す。具体的には次のようにする。

scala> implicit val z: Int = 2
z: Int = 2

scala> add(3)
res0: Int = 5

scala> sub(3)
res1: Int = 1

scala> mul(3)
res2: Int = 6

implicit val で宣言された変数 z が、メソッド呼び出しの時に暗黙に引き渡されている。

ここで、新しい変数を implicit で宣言したらどうなるだろう。

scala> implicit val z1: Int = 5
z1: Int = 5

scala> add(3)
<console>:15: error: ambiguous implicit values:
 both value z of type => Int
 and value z1 of type => Int
 match expected type Int
       add(3)
          ^

おや、エラーになった。どうやら、期待する Implicit Parameter にマッチする値が2つあるせいのようだ。こういう使い方はできないらしい。

明示的に渡してやったらどうだろう。

scala> add(3)(z1)
res4: Int = 8

ああ、これは普通にいけるのね。

というかさあ、こういうあちこちで使いまわす変数っていうのは、オブジェクト指向的にはオブジェクトのメンバー変数にしておくんだろうけど、Scala では違うんだろうか。