Scalaにインクリッツを連鎖させるにはどうすればよいですか?
-
26-10-2019 - |
質問
Pimp-My-Libraryパターンにより、そのクラスからメソッドを実装するクラスへの暗黙の変換を利用できるようにすることにより、クラスにメソッドを追加するように見えます。
Scalaはそのような2つの暗黙的な変換を許可していないので、私はから得ることができません 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)
}
編集:Scala 2.11以降、ビュー境界( '<%')が非推奨です https://issues.scala-lang.org/browse/si-7629 (代わりにタイプクラスを使用できます)
ただし、暗黙の定義に暗黙のパラメーター自体(ビューバウンド)が必要な場合、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
暗黙の変換として使用されています、 aToB
と toA
として渡されています 暗黙的なパラメーター, 、暗黙的な変換として連鎖する代わりに。
編集
関連する興味のある質問:
他のヒント
暗黙のパラメーターでサークルを構築できることに注意してください。ただし、これらはこれで示されているように、コンパイラによって検出されます。
class Wrap {
class A(implicit b : B)
class B(implicit c : C)
class C(implicit a : A)
implicit def c = new C
implicit def b = new B
implicit def a = new A
}
ただし、ユーザーに指定されたエラーは、可能な限り明確ではありません。文句を言うだけです could not find implicit value for parameter
3つの建設現場すべて。それは、それほど明白ではない場合に根本的な問題を曖昧にする可能性があります。
これがそうです パスも蓄積するコード。
import scala.language.implicitConversions
// Vertices
case class A(l: List[Char])
case class B(l: List[Char])
case class C(l: List[Char])
case class D(l: List[Char])
case class E(l: List[Char])
// Edges
implicit def ad[A1 <% A](x: A1) = D(x.l :+ 'A')
implicit def bc[B1 <% B](x: B1) = C(x.l :+ 'B')
implicit def ce[C1 <% C](x: C1) = E(x.l :+ 'C')
implicit def ea[E1 <% E](x: E1) = A(x.l :+ 'E')
def pathFrom(end:D) = end
pathFrom(B(Nil)) // res0: D = D(List(B, C, E, A))