Implicit Parameter のもう1つの使い方。順を追ってみていく。
何らかのリストの合計を求めるメソッド sum を考える。ポイントは何のリストかわからない(あるいは何のリストでもいいように)、というところだ。
Scala は静的型付け言語なので単純にはいかない。ではどうするかというと、まず、「足し合わせることができる」型を考える。Additive としよう。
scala> trait Additive[A] { | def plus(a: A, b: A): A | def zero: A | } defined trait Additive
Additive は型パラメータを持っていて、これが目的のリストに要素の型になる。また、実装はしていないがメソッドを2つ宣言している。
- plus :2つの値を足し合わせる
- zero :ゼロに相当する値を返す
だ。
次に、この型を継承して、具体的な型についてメソッドを実装する。String と Int について実装しよう。
scala> object StringAdditive extends Additive[String] { | def plus(a: String, b: String): String = a + b | def zero: String = "" | } defined object StringAdditive scala> object IntAdditive extends Additive[Int] { | def plus(a: Int, b: Int): Int = a + b | def zero: Int = 0 | } defined object IntAdditive
そして最後に sum メソッド。Additive を使って、足し合わせるように実装する。
scala> def sum[A](lst: List[A])(m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y)) sum: [A](lst: List[A])(m: Additive[A])A
これで出来上がり。2つ目の引数リストで Additive を受け取るところがポイント。使い方はこうする。
scala> sum(List(1, 2, 3))(IntAdditive) res0: Int = 6 scala> sum(List("abc", "def", "ghi"))(StringAdditive) res1: String = abcdefghi
うまく「合計」を計算できた。
さて、ここからが本題。何のリストを合計するのかはリストを見ればわかるのだから、StringAdditive とか IntAdditive を明示的に渡さなくてもうまくやってほしい。Implicit Parameter を使うとそれができる。
次のように、StringAdditive を IntAdditive の定義の前と、sum メソッドの最後に引数リストの m の前に implicit キーワードをつける。
scala> trait Additive[A] { | def plus(a: A, b: A): A | def zero: A | } defined trait Additive scala> implicit object StringAdditive extends Additive[String] { | def plus(a: String, b: String): String = a + b | def zero: String = "" | } defined object StringAdditive scala> implicit object IntAdditive extends Additive[Int] { | def plus(a: Int, b: Int): Int = a + b | def zero: Int = 0 | } defined object IntAdditive scala> def sum[A](lst: List[A])(implicit m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y)) sum: [A](lst: List[A])(implicit m: Additive[A])A
これで StringAdditive や IntAdditive を明示的に渡さなくてもよくなる。
scala> sum(List(1, 2, 3)) res0: Int = 6 scala> sum(List("abc", "def", "ghi")) res1: String = abcdefghi
このとおり。