2015-09-30 11 views
6

Mam pewne problemy z typami w Scali, a dokładniej z błędami w czasie kompilacji, gdy nie można znaleźć egzemplarza typeclass. Załóżmy, że mam typeclass TC i obiekt B[C], który ma instancję TC tylko wtedy, gdy C ma instancję TC. W Scali można to zapisać: implicit def btc[C](implicit ctc: TC[C]) = new TC[B[C]] { ... }. Gdy potrzebujesz TC[B[C]] dla C, dla której skalak nie może znaleźć instancji TC[C], skalak wyświetli błąd informujący, że nie może znaleźć TC[B[C]]. Chociaż jest to prawdą, w komunikacie o błędzie brakuje przyczyny, dla której scalac nie może znaleźć TC[B[C]], to znaczy, że nie może znaleźć TC[C].Uczyń niejawną adnotację Scala w bardziej precyzyjny sposób

Aby zilustrować problem, postanowiłem zrobić mały przykład zabawki gdzie TC jest PrettyPrintable, C jest Unit i B jest 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 
} 

Gdybym uruchomieniu aplikacji pojawia się błąd:

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

Jest to oczywiście prawda: scalac nie może znaleźć ładnej instancji wydruku dla typu Option[Unit]. Problem polega na tym, że sam błąd nie jest bardzo pomocny, ponieważ nie chodzi o to, że skalak nie może znaleźć instancji klasy typów, ale więcej, dlaczego nie może jej znaleźć. W tym przypadku powodem, dla którego Option[Unit] nie ma ładnej instancji drukowania, jest to, że Unit nie ma ładnej instancji wydruku, ale w przypadku bardziej złożonych przypadków może to być koszmar.

Moje pytanie brzmi: czy możliwe jest popełnienie błędów niejawnych, które nie są bardziej precyzyjne?

+0

To jest dobre. Zastanowiłem się kiedyś, jaki kontekst wyeksponować interpolatorowi na ciąg adnotacji, ale nie myślałem o niejawnej historii wyszukiwania. https://issues.scala-lang.org/browse/SI-7411 –

Odpowiedz

0

To byłoby miłe, ale myślę, że problem, który chcesz rozwiązać, jest bardziej skomplikowany niż na pierwszy rzut oka. W tym przykładzie dodałem trochę więcej ukrytych Vals i DEFS dla PrettyPrintable:

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 
} 

Ostatnia linia produkuje następujący błąd kompilatora, analogiczną do błędu ze swojego przykład:

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

w twojej W takim przypadku łatwo zrzucić winę za nieodnalezienie ukrytego PrettyPrintable[Option[Unit]] w przypadku nieodnalezienia ukrytego PrettyPrintable[Unit]. Ale w przypadku pary, czy chcesz winić, że nie znalazłeś ukrytego PrettyPrintable[(Float, Long)], nie znajdując niejawnego PrettyPrintable[Float], nie znajdując ukrytego PrettyPrintable[Long], czy obu?

Należy pamiętać, że podstawowa przyczyna niejawnego niepowodzenia rozdzielczości może nie mieć nawet takiego samego kształtu, jak oryginał. Na przykład, należy rozważyć dodanie następujących dwóch ukrytych DEFS:

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

Teraz jest przyczyną nie znalezienie niejawnego Int lub nie znalezienie niejawnego String? Rzeczywista odpowiedź jest bardzo skomplikowana.Coś takiego:

  • nie było niejawne PrettyPrintable[(Float,Long)] ponieważ:
    • nie było niejawna wartość z tym właśnie typu, a
    • nie było implicits zarówno dla PrettyPrintable[Float] i PrettyPrintable[Long]
      • nie było jednoznaczne z PrettyPrintable[Float], ponieważ:
        • nie było niejawna wartość z tym właśnie typu, a
        • nie było niejawne Int I
        • nie było niejawne String
      • nie było ukryte za PrettyPrintable[Long] ponieważ:
        • tam nie było wartością domyślną z tym dokładnym typem:

W odpowiedzi na pytanie „Czy to możliwe, aby błędy niejawna Nie znaleziono bardziej precyzyjny?”, Myślę, że wiązałoby zapewniając jakiś programowy dostęp do szerokiej drzewie możliwości dla Ciebie skonstruować komunikat o błędzie bardziej precyzyjnie w ogólnym przypadku. Samo zaprojektowanie przyzwoitego API do tej pracy byłoby imponującym przedsięwzięciem, nie mówiąc już o implementacji tego API.

+0

Kompilator może przycinać drzewa, takie jak powyższy, ze względu na wydajność. Na przykład kompilator prawdopodobnie nawet nie rozważył implikacji dla 'PrettyPrintable [Long]' gdy nie udało się znaleźć ukrytego dla 'PrettyPrintable [Float]' –

+0

Nie sądzę, że to pytanie jest dość skomplikowane. W przypadku 'PP [(Float, Long)], najlepiej komunikat o błędzie wskaże, które z tych dwóch wystąpień brakuje, lub oba, jeśli tak jest. A twoje ostatnie przykłady to niejawne konwersje, które przyjmują formę, której zwykle nie widzisz podczas pracy z klasami typów. –

+0

@TravisBrown Czy chcesz zaproponować rozwiązanie, które zajmie się ogólnym przypadkiem? Nawet w tym prostym przypadku sugerujesz, że komunikat o błędzie powinien "wskazać, które z tych dwóch wystąpień brakuje, lub oba, jeśli tak jest", jak zamierzasz stworzyć taki komunikat o błędzie? Jak daleko się posuniesz? Czy poradzisz sobie z 'PP [((((A, B), (C, D), E), (F, G))]? –

Powiązane problemy