2015-09-30 8 views
6

Ich habe einige Probleme mit Typklassen in Scala und genauer mit Fehlern zur Kompilierzeit, wenn eine Typklasseninstanz nicht gefunden werden kann. Nehmen wir an, ich habe eine Typklasse TC und ein Objekt B[C], das eine Instanz von TC nur dann hat, wenn C eine Instanz von TC hat. In Scala kann dies implicit def btc[C](implicit ctc: TC[C]) = new TC[B[C]] { ... } geschrieben werden. Wenn Sie eine TC[B[C]] für eine C benötigen, für die scalac keine Instanz TC[C] finden kann, gibt scalac eine Fehlermeldung aus, dass es keine TC[B[C]] finden kann. Obwohl dies der Fall ist, fehlt in der Fehlermeldung der Grund, warum scalac TC[B[C]] nicht finden kann, dh TC[C] kann nicht gefunden werden.Scalas implicitNotFound Annotation präzisieren

das Problem zu veranschaulichen, habe ich beschlossen, ein kleines Spielzeug Beispiel zu machen, wo TCPrettyPrintable ist, C ist Unit und B ist Option:

import scala.annotation.implicitNotFound 

@implicitNotFound("Cannot pretty print instances of the type ${T}") 
trait PrettyPrintable[T] { 
    def prettyPrint(t: T): String 
} 

object PrettyPrintable { 
    def apply[T](implicit pp: PrettyPrintable[T]): PrettyPrintable[T] = pp 

    implicit def optPP[T](implicit opp: PrettyPrintable[T]) = new PrettyPrintable[Option[T]] { 
    override def prettyPrint(ot: Option[T]): String = ot.fold("")(opp.prettyPrint) 
    } 
} 

object Main extends App { 
    println(PrettyPrintable[Option[Unit]].prettyPrint(None)) // error 
} 

Wenn ich die Anwendung ausführen, bekomme ich die Fehlermeldung:

[error] /home/rief/prog/scala/implicitNotFoundTest/Main.scala:24: Cannot pretty print instances of the type Option[Unit] 
[error] println(PrettyPrintable[Option[Unit]].prettyPrint(None)) 

Das ist natürlich wahr: scalac findet keine schöne Druckinstanz für den Typ Option[Unit]. Das Problem ist, dass der Fehler selbst nicht sehr hilfreich ist, weil der Punkt nicht ist, dass scalac eine Instanz der Typklasse nicht finden kann, sondern mehr, warum sie es nicht finden kann. In diesem Fall liegt der Grund, warum Option[Unit] keine hübsche Druckinstanz hat, darin, dass Unit keine hübsche Druckinstanz hat, aber für komplexere Fälle kann dies ein Albtraum sein.

Meine Frage ist: Ist es möglich, Fehler implizit nicht genauer zu finden?

+0

Das ist ein gutes ist. Ich hatte einmal darüber nachgedacht, welchen Kontext ich einem Interpolator für die Annotationskette zur Verfügung stellen sollte, aber ich hatte nicht über implizite Suchhistorie nachgedacht. https://issues.scala-lang.org/browse/SI-7411 –

Antwort

0

Das wäre nett, aber ich denke, das Problem, das Sie lösen wollen, ist ein bitterer, komplizierter als es auf den ersten Blick scheint. In diesem Beispiel habe ich einige mehr impliziten vals und defs für PrettyPrintable hinzugefügt:

import scala.annotation.implicitNotFound 

@implicitNotFound("Cannot pretty print instances of the type ${T}") 
trait PrettyPrintable[T] { 
    def prettyPrint(t: T): String 
} 

object PrettyPrintable { 
    def apply[T](implicit pp: PrettyPrintable[T]): PrettyPrintable[T] = pp 

    implicit val intPP = new PrettyPrintable[Int] { 
    override def prettyPrint(i: Int): String = s"== $i ==" 
    } 

    implicit def optPP[T](implicit opp: PrettyPrintable[T]) = new PrettyPrintable[Option[T]] { 
    override def prettyPrint(ot: Option[T]): String = s"-- ${ot.map(opp.prettyPrint)} --" 
    } 

    implicit def pairPP[T, U](implicit tpp: PrettyPrintable[T], upp: PrettyPrintable[U]) = 
    new PrettyPrintable[(T, U)] { 
     override def prettyPrint(pair: (T, U)): String = 
     s"[[[ ${tpp.prettyPrint(pair._1)} >>> ${upp.prettyPrint(pair._2)} ]]]" 
    } 

} 

object Main extends App { 
    println(PrettyPrintable[Int].prettyPrint(6))     // prints == 6 == 
    println(PrettyPrintable[Option[Int]].prettyPrint(None))  // prints -- None -- 
    println(PrettyPrintable[Option[Int]].prettyPrint(Some(6)))  // prints -- Some(== 6 ==) -- 
    println(PrettyPrintable[(Int,Int)].prettyPrint((6 -> 7)))  // prints [[[ == 6 == >>> == 7 == ]]] 
    println(PrettyPrintable[(Float,Long)].prettyPrint((6F -> 7L))) // error 
} 

Die letzte Zeile erzeugt die folgenden Compiler-Fehler, analog zu dem Fehler aus Ihrem Beispiel:

Cannot pretty print instances of the type (Float, Long) 

in Ihrem Fall ist es leicht, die Schuld dafür zu werfen, dass man kein implizites PrettyPrintable[Option[Unit]] gefunden hat, wenn man kein implizites PrettyPrintable[Unit] findet. Aber im Fall des Paares, möchten Sie die Schuld nicht finden, eine implizite PrettyPrintable[(Float, Long)] auf nicht finden eine implizite PrettyPrintable[Float], nicht finden eine implizite PrettyPrintable[Long] oder beides?

Beachten Sie, dass die Ursache des impliziten Auflösungsfehlers möglicherweise nicht die gleiche Form wie das Original hat. Betrachten wir zum Beispiel die folgenden zwei implizite defs Zugabe:

implicit def i2ppf(implicit i: Int) = new PrettyPrintable[Float] { 
    override def prettyPrint(f: Float): String = s"xx $f xx" 
} 

implicit def s2ppf(implicit s: String) = new PrettyPrintable[Float] { 
    override def prettyPrint(f: Float): String = s"xx $f xx" 
} 

Nun ist die Ursache eine implizite Int, oder nicht finden, eine implizite String nicht finden? Die tatsächliche Antwort ist sehr kompliziert.Etwas wie folgt aus:

  • gab es keine implizite PrettyPrintable[(Float,Long)] weil:
    • gab es keine implizite Wert mit genau diesem Typ und
    • es nicht implicits für beide PrettyPrintable[Float] und PrettyPrintable[Long]
      • waren es gab keine implizite für PrettyPrintable[Float] weil:
        • gibt es keinen impliziten Wert mit genau diesem Typ und
        • gibt es keine impliziten Int UND
        • gab es keine impliziten String
      • gibt es keine impliziten für PrettyPrintable[Long] weil:
        • dort war kein impliziter Wert mit diesem präzisen Typ

In Antwort auf Ihre Frage, „ist es möglich, Fehler auf implizite machen nicht genauer gefunden?“, Denke ich, dass sich bringen würde Sie irgendeine Art von programmatischen Zugang zu einem breiten Baum von Möglichkeiten für Sie bereitstellt im allgemeinen Fall eine Fehlermeldung genauer zu konstruieren. Nur eine vernünftige API für den Job zu entwickeln wäre ein ziemlich beeindruckendes Unterfangen, geschweige denn eine Implementierung dieser API.

+0

Der Compiler kann auch aus Performance-Gründen über Bäume wie die Pflaume. Zum Beispiel hat der Compiler wahrscheinlich nicht einmal implitits für 'PrettyPrintable [Long]' in Betracht gezogen, wenn es nicht implizit für 'PrettyPrintable [Float]' –

+0

gefunden hat. Ich denke nicht, dass die Frage so kompliziert ist. Im Fall von 'PP [(Float, Long)]', im Idealfall würde die Fehlermeldung an, welche der beiden Instanzen fehlen, oder beides, wenn das der Fall ist. Und Ihre letzteren Beispiele sind implizite Konvertierungen, die eine Form annehmen, die Sie normalerweise nicht sehen, wenn Sie mit Typklassen arbeiten. –

+0

@TravisBrown haben Sie eine Lösung vorschlagen möchten, die den allgemeinen Fall behandeln wird? Selbst im einfachen Fall schlagen Sie vor, dass die Fehlermeldung "anzeigt, welche der beiden Instanzen fehlt oder beides, wenn das der Fall ist". Wie werden Sie eine solche Fehlermeldung erstellen? Wie weit wirst du es nehmen? Gehst du 'PP [(((A, B), (C, D), E), (F, G))]' zu behandeln? –

Verwandte Themen