user3248346
user3248346

Reputation:

How To Customise Implicit Not Found Message

I would like to replace the standard implicit not found message when using the =:= operator in the following example:

sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean

class Builder[X <: TBoolean, Y <: TBoolean, Z <: TBoolean] private(x: Int, y: Int, z: List[List[Int]]) {
  protected def this() = this(-1, -1, List.empty)

  def withX(xx: Int)(implicit ev: X =:= TFalse) = new Builder[TTrue, Y, Z](xx, this.y, this.z)

  def withY(yy: Int)(implicit ev: Y =:= TFalse) = new Builder[X, TTrue, Z](this.x, yy, this.z)

  def withZ(zz: List[List[Int]])(implicit ev: Z =:= TFalse) = new Builder[X, Y, TTrue](this.x, this.y, zz)

  def build()(implicit ev1: Y =:= TTrue, ev2: X =:= TTrue, ev3: Z =:= TTrue) = println(s"${x} ${y} ${z}")
}

object Builder {
  def apply() = new Builder[TFalse, TFalse, TFalse]
}

If I try to build an illegal object like this:

Builder().withX(1).withY(1).withZ(List.empty).withY(1).build   // i.e. two withY calls

I get the usual:

error: Cannot prove that TTrue =:= TFalse.

I would like to customise this message so that it is more informative. I tried using @scala.annotation.implicitNotFound("custom message: blah") but that doesn't seem to work.

Is there a way to do this?

Upvotes: 2

Views: 354

Answers (2)

Mario Galic
Mario Galic

Reputation: 48420

You could change it for a particular method by inlining annotation like so

def g[A](i: A)(implicit @implicitNotFound("You should not be a ${A}") ev: A =:= Int) = ???

g("") // Error: You should not be a String

but of course that will work for just that method.

Upvotes: 5

Ivan Kurchenko
Ivan Kurchenko

Reputation: 4063

According to implicitNotFound annotation Scaladoc (https://www.scala-lang.org/api/2.12.5/scala/annotation/implicitNotFound.html) :

... cannot be found, annotate the class C with @implicitNotFound.

So, in case of using =:= class, which has this anotation, you can not override it, unless you will implement own type equality prove type class, with your own error message:

@implicitNotFound(msg = "Custom type equals prove message ${From} =::= ${To}.")
    sealed abstract class =::=[From, To]
    private final val singleton_=::= = new =::=[Any,Any] {}
    object =::= {
      implicit def tpEquals[A]: A =::= A = singleton_=::=.asInstanceOf[A =::= A]
    }

and use it:

def withZ(zz: List[List[Int]])(implicit ev: Z =::= TFalse)

So the result error message will be:

Custom type equals prove message TTrue =::= TFalse.

Hope this helps!

Upvotes: 4

Related Questions