Implicitの探索範囲

Implicit Conversion や Implicit Parameter が探索される範囲には、次のような範囲がある。

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

ここでは、コンパニオンオブジェクトに Implicit を定義するパターンを見てみる。

新しく Rational (有理数)型を定義するとして、次のように定義する。ここでは Rational.scala ファイルに書いた。

trait Additive[A] {
    def plus(a: A, b: A): A
    def zero: A
}

case class Rational(num: Int, den: Int)

object Rational {
    implicit object RationaAdditive extends Additive[Rational] {
        def plus(a: Rational, b: Rational): Rational = {
            if (a == zero) {
                b
            } else if (b == zero) {
                a
            } else {
                Rational(a.num * b.den + b.num * a.den, a.den * b.den)
            }
        }
        def zero: Rational = Rational(0, 0)
    }
}

同じディレクトリで sbt console を起動すると自動的に読み込まれる。

で、sum メソッドを定義。なんでファイルに書かないかというと、クラスやトレイトに属さないメソッドはファイルには書けない(たぶん)から。

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

さて、これで準備は完了。有理数の合計を求めてみる。

scala> sum(List(Rational(1, 2), Rational(1, 3)))
res0: Rational = Rational(5,6)

ちゃんと計算できた。

コンパニオンオブジェクトに Implicit を定義しておくのはわかりやすいかもしれないな。覚えておこう。