76 votes

Comment puis-je enchaîner implicite dans Scala?

Le modèle pimp-my-library me permet apparemment d'ajouter une méthode à une classe en rendant disponible une conversion implicite de cette classe en une qui implémente la méthode.

Scala ne permet pas deux de ces conversions implicites qui a lieu, cependant, donc je ne peux pas obtenu de A à C en utilisant un implicite A à B et un autre B C implicite. Y a-t-il un moyen de contourner cette restriction?

106voto

Daniel C. Sobral Points 159554

Scala a une restriction sur les conversions automatiques pour ajouter une méthode, qui est de ne pas appliquer plus d'une conversion en essayant de trouver des méthodes. Par exemple:

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

Toutefois, si une définition implicite nécessite un paramètre implicite, lui-même, Scala va chercher d'autres valeurs implicites pour aussi longtemps que nécessaire. Continuer à partir de l'exemple précédent:

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

"De la magie!", vous pourriez dire. De ne pas faire. Voici comment le compilateur pourrait traduire chacun:

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

Ainsi, alors que bToC est utilisé comme une conversion implicite, aToB et toA sont passés comme paramètres implicites, au lieu d'être enchaîné comme les conversions implicites.

MODIFIER

Liés à la question de l'intérêt:

12voto

Raphael Points 1262

Notez que vous pouvez également créer des cercles avec des paramètres implicites. Celles-ci sont toutefois détectées par le compilateur, comme indiqué par ceci:

 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
}
 

Les erreurs commises à l’utilisateur ne sont pas aussi claires qu’elles pourraient l’être; il se plaint simplement could not find implicit value for parameter pour les trois sites de construction. Cela pourrait masquer le problème sous-jacent dans des cas moins évidents.

1voto

Sagie Davidovich Points 273

Voici un code qui accumule également le chemin.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X