Wie kann ich Implikationen in Scala ketten?
-
26-10-2019 - |
Frage
Das Pimp-My-Bibliothek-Muster ermöglicht es mir, einer Klasse scheinbar eine Methode hinzuzufügen, indem ich eine implizite Konvertierung von dieser Klasse zu einer zur implementierenden Methode zur Verfügung stellt.
Scala erlaubt jedoch nicht zwei derartige implizite Konvertierungen, daher kann ich nicht von mir kommen A
zu C
Verwenden eines impliziten A
zu B
und ein anderer implizit B
zu C
. Gibt es einen Weg um diese Einschränkung?
Lösung
Scala hat eine Beschränkung für automatische Konvertierungen, um eine Methode hinzuzufügen, nämlich nicht mehr als eine Konvertierung beim Versuch, Methoden zu finden. 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: Ansicht Grenzen ('<%') sind veraltet, da Scala 2.11 https://issues.scala-lang.org/browse/si-7629 (Sie können stattdessen Typklassen verwenden)
Wenn eine implizite Definition jedoch einen impliziten Parameter selbst (Ansicht gebunden) erfordert, scala Wille Suchen Sie nach zusätzlichen impliziten Werten so lange nach Bedarf. Fahren Sie vom 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!", Vielleicht sagen Sie. Nicht so. Hier ist, wie der Compiler jeden übersetzt:
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
wird als implizite Konvertierung verwendet, aToB
und toA
werden als bestanden als implizite Parameter, anstatt als implizite Konvertierungen gekettet zu werden.
BEARBEITEN
Verwandte Frage von Interesse:
Andere Tipps
Beachten Sie, dass Sie auch Kreise mit impliziten Parametern erstellen können. Diese werden jedoch vom Compiler festgestellt, wie dies ausgestellt wird:
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 angegebenen Fehler sind jedoch nicht so klar wie möglich; 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.
Hier ist Ein Code, der auch den Pfad sammelt.
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))