Come posso concatenare impliciti a Scala?
-
26-10-2019 - |
Domanda
Il modello magnaccia-my-biblioteca mi permette di aggiungere un metodo apparentemente ad una classe mettendo a disposizione una conversione implicita da quella classe a uno che implementa il metodo.
Scala non consente due tali conversioni implicite in atto, però, quindi non posso ottenuto da A
a C
utilizzando un A
implicito B
e un altro B
implicita C
. C'è un modo per aggirare questa limitazione?
Soluzione
Scala ha una restrizione conversioni automatiche per aggiungere un metodo, che è che non applicherà più di una conversione nel tentativo di trovare metodi. Ad esempio:
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: View limiti ( '<%') sono deprecati dal Scala 2.11 https://issues.scala-lang.org/browse/SI-7629 (È possibile utilizzare le classi di tipo invece)
Tuttavia, se una definizione implicita richiede un parametro implicito in sé (View legato), Scala look per valori impliciti aggiuntivi per tutto il tempo necessario. Continuare dall'ultimo esempio:
// 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)
}
"Magic!", Si potrebbe dire. Non così. Ecco come il compilatore tradurrebbe ognuno:
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)
}
Così, mentre bToC
viene utilizzato come una conversione implicita, aToB
e toA
vengono passati come parametri impliciti , invece di essere incatenato come conversioni implicite.
Modifica
questione connessa di interesse:
Altri suggerimenti
Si noti che si può costruire cerchi con parametri impliciti, anche. Questi sono, tuttavia, rilevato dal compilatore, come presentato da questo:
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
}
L'errore (s) trasmesso all'utente non è chiaro come potrebbero essere, comunque; solo lamenta could not find implicit value for parameter
per tutti e tre sito di costruzione. Questo potrebbe oscurare il problema di fondo nei casi meno evidenti.
Ecco un codice che si accumula anche il percorso.
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))