Как я могу неявные преобразования в Scala?
шаблон pimp-my-library позволяет мне, по-видимому, добавить метод в класс, сделав доступным неявное преобразование из этого класса в тот, который реализует метод.
Scala не допускает двух таких неявных преобразований, однако, поэтому я не могу получить от A
до C
используя неявную A
до B
и еще один неявный B
до C
. Есть ли способ обойти это ограничение?
3 ответа:
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: границы просмотра ( '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
передается как неявных параметров, вместо того, чтобы быть прикованным как неявные преобразования.EDIT
связанный с этим вопрос, представляющий интерес:
обратите внимание, что вы также можете создавать круги с неявными параметрами. Они, однако, обнаруживаются компилятором, как показано этим:
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
для всех трех стройке. Это может затмить основную проблему в менее очевидных случаях.
здесь код, который также накапливает путь.
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))