Pregunta

El patrón Pimp-My-Library me permite agregar un método a una clase al poner a disposición una conversión implícita de esa clase a una que implementa el método.

Sin embargo, Scala no permite dos de esas conversiones implícitas, por lo que no puedo obtener de A a C Usando un implícito A a B y otro implícito B a C. ¿Hay alguna manera de evitar esta restricción?

¿Fue útil?

Solución

Scala tiene una restricción en las conversiones automáticas para agregar un método, que es que no aplicará más de una conversión al tratar de encontrar métodos. Por ejemplo:

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

Editar: Ver límites ('<%') están en desuso desde Scala 2.11 https://issues.scala-lang.org/browse/si-7629 (Puede usar clases de tipo en su lugar)

Sin embargo, si una definición implícita requiere un parámetro implícito (ver encuadernado), Scala voluntad Busque valores implícitos adicionales durante el tiempo que sea necesario. Continuar desde el último ejemplo:

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

"¡Magia!", Se podría decir. No tan. Así es como el compilador traduciría cada uno:

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

Entonces, mientras bToC se está utilizando como una conversión implícita, aToB y toA están siendo aprobados como Parámetros implícitos, en lugar de estar encadenado como conversiones implícitas.

EDITAR

Cuestión de interés relacionada:

Otros consejos

Tenga en cuenta que también puede construir círculos con parámetros implícitos. Sin embargo, son detectados por el compilador, como lo exhibe esto:

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
}

Sin embargo, los errores dados al usuario no son tan claros como podrían ser; Simplemente se queja could not find implicit value for parameter para los tres sitios de construcción. Eso podría oscurecer el problema subyacente en casos menos obvios.

Aquí es Un código que también acumula la ruta.

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))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top