Как я могу цепорить в Scala?
-
26-10-2019 - |
Вопрос
Паттерн сутенера-my-библиотека позволяет мне, по-видимому, добавить метод в класс, сделав доступное неявное преобразование из этого класса в тот, который реализует метод.
Однако я не допускает двух таких неявных конверсий, поэтому я не могу получить от 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)
}
Редактировать: View Bounds ('<%') устарели, поскольку Scala 2.11 https://issues.scala-lang.org/browse/si-7629 (Вместо этого вы можете использовать классы типов)
Однако, если неявное определение требует самого неявного параметра (просмотр Bound), 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
Для всех трех строительных площадок. Это может скрыть основную проблему в менее очевидных случаях.
Вот Код, который также накапливает путь.
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))