Das pimp-my-library-Muster ermöglicht es mir, einer Klasse scheinbar eine Methode hinzuzufügen, indem ich eine implizite Konvertierung von dieser Klasse in eine Klasse zur Verfügung stelle, die die Methode implementiert.
Scala erlaubt jedoch nicht, dass zwei solche impliziten Konvertierungen stattfinden, sodass ich nicht A
dazu kommen kann C
, ein implizites A
zu B
und ein anderes implizites B
zu zu verwenden C
. Gibt es einen Weg, um diese Einschränkung zu umgehen?
scala
implicit-conversion
implicits
Daniel C. Sobral
quelle
quelle
Antworten:
Scala hat eine Einschränkung für automatische Konvertierungen zum Hinzufügen einer Methode. Dies bedeutet, dass beim Versuch, Methoden zu finden, nicht mehr als eine Konvertierung angewendet wird. Zum Beispiel:
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) }
BEARBEITEN: Ansichtsgrenzen ('<%') sind seit Scala 2.11 veraltet. Https://issues.scala-lang.org/browse/SI-7629 (Sie können stattdessen Typklassen verwenden.)
Wenn jedoch eine implizite Definition einen impliziten Parameter erfordert selbst (Ansicht gebunden), Scala wird für zusätzliche implizite Werte für aussehen so lange wie nötig. Fahren Sie mit dem letzten Beispiel fort:
// 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) }
"Magie!", Könnte man sagen. Nicht so. So würde der Compiler jeden einzelnen übersetzen:
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) }
Also, während
bToC
es als implizite Konvertierung verwendet wirdaToB
undtoA
als implizite Parameter übergeben wird , anstatt als implizite Konvertierungen verkettet zu werden.BEARBEITEN
Verwandte Frage von Interesse:
quelle
aToB
undbToC
in,T2
als ich es in REPL versuchte.Beachten Sie, dass Sie auch Kreise mit impliziten Parametern erstellen können. Diese werden jedoch vom Compiler erkannt, wie dies zeigt:
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 }
Die dem Benutzer gegebenen Fehler sind jedoch nicht so klar wie sie sein könnten; es beschwert sich nur
could not find implicit value for parameter
für alle drei Baustellen. Dies könnte das zugrunde liegende Problem in weniger offensichtlichen Fällen verschleiern.quelle
Hier ist ein Code, der auch den Pfad akkumuliert.
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))
quelle