Как я могу неявные преобразования в Scala?


шаблон pimp-my-library позволяет мне, по-видимому, добавить метод в класс, сделав доступным неявное преобразование из этого класса в тот, который реализует метод.

Scala не допускает двух таких неявных преобразований, однако, поэтому я не могу получить от A до C используя неявную A до B и еще один неявный B до C. Есть ли способ обойти это ограничение?

3 74

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))