Frage

Das Pimp-My-Bibliothek-Muster ermöglicht es mir, einer Klasse scheinbar eine Methode hinzuzufügen, indem ich eine implizite Konvertierung von dieser Klasse zu einer zur implementierenden Methode zur Verfügung stellt.

Scala erlaubt jedoch nicht zwei derartige implizite Konvertierungen, daher kann ich nicht von mir kommen A zu C Verwenden eines impliziten A zu B und ein anderer implizit B zu C. Gibt es einen Weg um diese Einschränkung?

War es hilfreich?

Lösung

Scala hat eine Beschränkung für automatische Konvertierungen, um eine Methode hinzuzufügen, nämlich nicht mehr als eine Konvertierung beim Versuch, Methoden zu finden. Zum Beispiel:

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

Bearbeiten: Ansicht Grenzen ('<%') sind veraltet, da Scala 2.11 https://issues.scala-lang.org/browse/si-7629 (Sie können stattdessen Typklassen verwenden)

Wenn eine implizite Definition jedoch einen impliziten Parameter selbst (Ansicht gebunden) erfordert, scala Wille Suchen Sie nach zusätzlichen impliziten Werten so lange nach Bedarf. Fahren Sie vom letzten Beispiel fort:

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

"Magie!", Vielleicht sagen Sie. Nicht so. Hier ist, wie der Compiler jeden übersetzt:

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

Also, während bToC wird als implizite Konvertierung verwendet, aToB und toA werden als bestanden als implizite Parameter, anstatt als implizite Konvertierungen gekettet zu werden.

BEARBEITEN

Verwandte Frage von Interesse:

Andere Tipps

Beachten Sie, dass Sie auch Kreise mit impliziten Parametern erstellen können. Diese werden jedoch vom Compiler festgestellt, wie dies ausgestellt wird:

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
}

Die dem Benutzer angegebenen Fehler sind jedoch nicht so klar wie möglich; Es beschwert sich nur could not find implicit value for parameter Für alle drei Baustellen. Dies könnte das zugrunde liegende Problem in weniger offensichtlichen Fällen verschleiern.

Hier ist Ein Code, der auch den Pfad sammelt.

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))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top