1. ホーム
  2. scala

Scalaでimplicitを連鎖させるには?

2023-08-10 07:50:33

質問

pimp-my-library パターンは、クラスからそのメソッドを実装するクラスへの暗黙の変換を利用可能にすることで、クラスにメソッドを追加するように見せかけることができます。

Scala はこのような暗黙の変換を二つ行うことを許しません. A から C を使用して、暗黙の A から B と、もう一つの暗黙の B から C . この制限を回避する方法はありますか?

どのように解決するのですか?

Scalaでは、メソッドを追加するための自動変換に制限があり、メソッドを探そうとしても複数の変換を適用しないようになっています。例えば

class A(val n: Int)
class B(val m: Int, val n: Int)
class C(val m: Int, val n: Int, val o: Int) {
  def total = m + n + o
}

// This demonstrates implicit conversion chaining restrictions
object T1 { // to make it easy to test on REPL
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB(a: A): B = new B(a.n, a.n)
  implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)

  // won't work
  println(5.total)
  println(new A(5).total)

  // works
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}

EDIT: Scala 2.11からビュー境界('<%')は非推奨になりました。 https://issues.scala-lang.org/browse/SI-7629 (代わりに型クラスが使えます)

ただし,暗黙の定義が暗黙のパラメータ自身を必要とする場合(View bound),Scalaでは は必要な限り追加の暗黙の値を探します。最後の例から続ける。

// def m[A <% B](m: A) is the same thing as
// def m[A](m: A)(implicit ev: A => B)

object T2 {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)

  // works
  println(5.total)
  println(new A(5).total)
  println(new B(5, 5).total)
  println(new C(5, 5, 10).total)
}

マジックだ!」と言われるかもしれません。そうではありません。以下は、コンパイラがそれぞれをどのように翻訳するかです。

object T1Translated {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB(a: A): B = new B(a.n, a.n)
  implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n)

  // Scala won't do this
  println(bToC(aToB(toA(5))).total)
  println(bToC(aToB(new A(5))).total)

  // Just this
  println(bToC(new B(5, 5)).total)

  // No implicits required
  println(new C(5, 5, 10).total)
}

object T2Translated {
  implicit def toA(n: Int): A = new A(n)
  implicit def aToB[A1 <% A](a: A1): B = new B(a.n, a.n)
  implicit def bToC[B1 <% B](b: B1): C = new C(b.m, b.n, b.m + b.n)

  // Scala does this
  println(bToC(5)(x => aToB(x)(y => toA(y))).total)
  println(bToC(new A(5))(x => aToB(x)(identity)).total)      
  println(bToC(new B(5, 5))(identity).total)

  // no implicits required
  println(new C(5, 5, 10).total)
}

では、一方 bToC は暗黙の変換として使われています。 aToBtoA として渡されます。 暗黙のパラメータ として渡され、暗黙の変換として連結されるのではありません。

EDIT

気になる関連質問。