テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(scala report) implicit conversionは連鎖しない

ScalaのImplicit conversion(と, Implicit parameter, implicitly)は, ライブラリの中で大変良く使われるパターンである(そして他の言語にはあまり見られない特長である). これについて調査している中でふと思った疑問について調べた.

Implicit conversion A->BとB->Cがあった時に, Aのインスタンスは暗黙的にCに変換されるか?

答えはNo. 以下のコードで, A->BとB->Cは変換が出来ているが, A->Cは出来ない.

object App {
  class A(val x: Int)
  implicit class B(a: A)
  implicit class C(b: B)
  def main(args: Array[String]) = {
    val a = new A(1)
    val b_of_a: B = a // OK
    val c_of_b: C = b_of_a // OK
    val c_of_a: C = a // NG
  }
}

以下は想像.

これは, Implicit conversionが, 単に暗黙的なA->Cをダイレクトに探すアルゴリズムで実装されているからだろう. しかし直感的には, 渡されたAから辿れるBが見つかり, Bから辿れるCが見つかることによってA->Cを導出出来ても良いはずだと思う. AからはBの他にも見つかる可能性があり, Bからも同様にC以外に見つかる可能性があるが, 現実的には候補が多くないため, このように実装して利便性を重視しても良いと思った. ループを回避するためには同じノードを辿らなければよい.

しかしそうなっていない理由としては以下が考えられる.

  • Implicit conversionはただでさえ分かりにくいのに, 連鎖すると大変分かりにくくなるから.
  • A->Cを導出するのに, A->B, B->Cと, A->Cの両方が見つかった場合はどうするのか?この場合はダイレクトなA->Cを採用するのが自然である. しかし, A->Dを導出するのに, A->B->Dとたどるのと, A->C->Dとたどる両方が見つかった場合はどうするのか?自然な解はない.