That would be nice, but I think that the problem you want to solve is much more complicated than it seems at first glance. In this example, I added some more implicit vals and defs for 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 }
The last line produces the following compiler error, similar to the error from your example:
Cannot pretty print instances of the type (Float, Long)
In your case, it's easy to blame for not finding the implicit PrettyPrintable[Option[Unit]] without finding the implicit PrettyPrintable[Unit] . But in the case of a couple, would you like to blame that you did not find the implicit PrettyPrintable[(Float, Long)] without detecting the implicit PrettyPrintable[Float] , without finding the implicit PrettyPrintable[Long] or both?
Please note that the main reason for implicit permission cannot even have the same form as the original. For example, consider adding the following two implicit 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" }
Now, is there a reason not to find the implicit Int , or not to find the implicit String ? The actual answer is very complex. Something like that:
- There was no implicit
PrettyPrintable[(Float,Long)] , because:- there was no implicit value with this exact type, AND
- there were no signs for
PrettyPrintable[Float] and PrettyPrintable[Long]PrettyPrintable[Float] not implied, because:- there was no implicit value with this exact type, AND
- there was no implicit
Int , and - there was no implicit
String
PrettyPrintable[Long] not implied, because:- there was no implicit value with this exact type
In response to your question: βIs it possible to make errors implicitly, no more accurate?β, I think this is due to giving you a certain programmatic access to a wide tree of possibilities for creating an error message more precisely in the general case. Just developing a decent API to work with would be a pretty impressive start, not to mention implementing that API.