After revisiting the "contrainvariance" thread today and some discussion with Daniel Spiewak, I decided to play around a bit with implicit resolution, and found some behavior that surprised me a little. It probably won't surprise a lot of people, but it did me even after all this time. A REPL session follows:
scala> class A
defined class A
scala> class B extends A
defined class B
scala> class C extends A
defined class C
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Foo[+A]
implicit val fooa = new Foo[A]
implicitly[Foo[A]]
// Exiting paste mode, now interpreting.
defined class Foo
fooa: Foo[A] = Foo@72a5ce92
res0: Foo[A] = Foo@72a5ce92
... okay, this is exactly what I expect. So far, so good.
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit val fooa = new Foo[A]
implicit val foob = new Foo[B]
implicitly[Foo[A]]
// Exiting paste mode, now interpreting.
fooa: Foo[A] = Foo@670064a4
foob: Foo[B] = Foo@4acf7fd0
res1: Foo[A] = Foo@4acf7fd0
... Interesting. I asked for a Foo[A], and it gave me a Foo[B] because that's the most specific, or narrowest-typed thing that the compiler could find that satisfied the signature. I had kind of expected this to be ambiguous though. After all, I asked for a Foo[A] and there are two valid implicits satisfying that signature.
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit val fooa = new Foo[A]
implicit val foob = new Foo[B]
implicit val fooc = new Foo[C]
implicitly[Foo[A]]
// Exiting paste mode, now interpreting.
<console>:21: error: ambiguous implicit values:
both value fooc in object $iw of type => Foo[C]
and value foob in object $iw of type => Foo[B]
match expected type Foo[A]
implicitly[Foo[A]]
^
... If this is ambiguous, as expected, why was the former not so? In both cases, you have more than one implicit in scope that can satisfy the signature, but here because there's more than one "most specific"-typed implicit available, it complains of ambiguity. Further exploration shows that contravariance is treated in exactly the same fashion; the compiler does not complain of ambiguity so long as the hierarchy of implicits in scope is a tree with only one branch.
scala> trait X
defined trait X
scala> trait Y
defined trait Y
scala> trait Z extends X with Y
defined trait Z
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Bar[-A]
implicit val barz = new Bar[Z]
implicitly[Bar[Z]]
// Exiting paste mode, now interpreting.
defined class Bar
barz: Bar[Z] = Bar@65f102c1
res3: Bar[Z] = Bar@65f102c1
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit val barx = new Bar[X]
implicit val barz = new Bar[Z]
implicitly[Bar[Z]]
// Exiting paste mode, now interpreting.
barx: Bar[X] = Bar@73d742a1
barz: Bar[Z] = Bar@39579371
res4: Bar[Z] = Bar@73d742a1
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit val barx = new Bar[X]
implicit val bary = new Bar[Y]
implicit val barz = new Bar[Z]
implicitly[Bar[Z]]
// Exiting paste mode, now interpreting.
<console>:27: error: ambiguous implicit values:
both value barx in object $iw of type => Bar[X]
and value bary in object $iw of type => Bar[Y]
match expected type Bar[Z]
implicitly[Bar[Z]]
... Well, at least this is consistent with what we've seen before in the case of covariance. But it puzzles me that implicit search doesn't consider the 2-valid-implicits-in scope cases to be ambiguous, while the 3-in-scope cases are so. After all, naively it seems that there would be a rational choice in each ambiguous case; that being the fooa and barz implicits, which don't even appear to be considered in the search if there is any implicit for a subtype available. And, this seems to me to be the issue with contravariant typeclasses like Ordering; it's not that the compiler *couldn't* find the correct implicit if it were the only one in scope, it's that it will actively seek out the "wrong" one (wrong from the perspective of the user, at least.) So, could a solution be to make the rules surrounding ambiguity stronger, so that the 2-valid-implicits cases are also rejected as ambiguous?