Reputation: 325
I'm trying to implement an implicit Not[T]
in Scala, and as such I want to throw an compiler error when there is an implicit val in scope of type T
. I thought of doing this using ambiguous implicits as the Scaladoc shows a way to implement =!=
. (See below)
But, I don't understand why two newAmbig
s are necessary, because there still seems to be an ambiguous implicit if I remove one, as there originally seems to be 3 viable implicits. (See below)
I could not find any documentation on what is required for the compiler to flag an ambiguous implicit.
The implementation of =!=
shown by the Scaladoc:
trait =!=[C, D]
implicit def neq[E, F] : E =!= F = null
@annotation.implicitAmbiguous("Could not prove ${J} =!= ${J}")
implicit def neqAmbig1[G, H, J] : J =!= J = null
implicit def neqAmbig2[I] : I =!= I = null
implicitly[Int =!= Int]
The implementation of =!=
that seems to work but does not:
trait =!=[C, D]
implicit def neq[E, F] : E =!= F = null
@annotation.implicitAmbiguous("Could not prove ${J} =!= ${J}")
implicit def neqAmbig1[G, H, J] : J =!= J = null
implicitly[Int =!= Int]
As both the neqAmbig1
and neq
should be of the same type, and both should be found.
But, this doesn't throw a compiler error, and when tested, just returns null
.
Upvotes: 3
Views: 175
Reputation: 48400
isAsSpecific seems to be the relevant part of the compiler that codifies overloading resolution rules from the spec, so see if you can decipher those:
/** Is type `ftpe1` strictly more specific than type `ftpe2`
* when both are alternatives in an overloaded function?
* @see SLS (sec:overloading-resolution)
*/
def isAsSpecific(ftpe1: Type, ftpe2: Type): Boolean
To confirm your first implementation is correct consider Type inequality as provided by shapeless
// Type inequalities
trait =:!=[A, B] extends Serializable
implicit def neq[A, B] : A =:!= B = new =:!=[A, B] {}
implicit def neqAmbig1[A] : A =:!= A = unexpected
implicit def neqAmbig2[A] : A =:!= A = unexpected
For example,
import shapeless.{<:!<, =:!=}
def foo[A](a: A)(implicit ev: A =:!= String): A = a
foo(3) // ok
foo("") // error: ambiguous implicit values
Upvotes: 2
Reputation: 51271
Let's start by simplifying and clarifying the situation.
This works. The implicit is resolved as expected.
trait =!=[C, D]
implicit def neq[E, F] : E =!= F =
new =!=[E,F]{override def toString="NEQ"}
implicitly[Int =!= Int] //res0: Int =!= Int = NEQ
Now let's add what should be an ambiguous implicit.
trait =!=[C, D]
implicit def neq[E, F] : E =!= F =
new =!=[E,F]{override def toString="NEQ"}
implicit def neqAmbig1[E, F] : E =!= E =
new =!=[E,E]{override def toString="NeqAm1"}
implicitly[Int =!= Int] //res0: Int =!= Int = NeqAm1
Hmmm. In the case of Int =!= Int
we have E
as Int
, and F
as Int
, so type E =!= F
should be the same as E =!= E
, and yet the compiler does not consider them to be equivalent and chooses the E =!= E
version (no matter what order the implicits are defined in the code).
I believe what's going on here is described in the language spec:
If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution.
This is from the section on Implicit Parameters but I think it still applies: type E =!= E
is more specific to Int =!= Int
than type E =!= F
is.
Upvotes: 2