4

I'm wondering if this is a bug or an expected behaviour in Scala 2.13-M5.

Following snippet compiles and outputs "object in package object":

package object test { implicit val a: TS = new TS("val in package object") implicit object b extends TS("object in package object") } package test { class TS(override val toString: String) class Inner { implicit val f: TS = new TS("val in inner class") val resolve = implicitly[TS] } object Test extends App { println(new Inner().resolve) } } 

With commented out third line implicit object b extends TS("object in package object" there is an ambiguous implicit compile error which is what I would expect in first case as well:

Error:(11, 29) ambiguous implicit values: both value a in package test of type => test.TS and value f in class Inner of type => test.TS match expected type test.TS val resolve = implicitly[TS] 
0

1 Answer 1

3

In your example, a, b and f all seem to be eligible implicit values. As the FAQ says (emphasis mine):

[...] this entails selecting a narrower type or a value defined in a subclass relative to other eligible values

therefore, the object b is selected, because b.type is a strict subtype of TS.


Here is another example that demonstrates the same behavior, but without packages or objects:

case class TS(str: String) object test { implicit val a: TS = new TS("val in package object") // implicit object b extends TS("object in package object") class MoreSpecial() extends TS("I'm special") implicit val s: MoreSpecial = new MoreSpecial() class TS(override val toString: String) class Inner { implicit val f: TS = new TS("val in inner class") val resolve = implicitly[TS] } object Test { def run(): Unit = { println(new Inner().resolve) } } } test.Test.run() 

it will print "I'm special", because the instance of class MoreSpecial thinks that it's the most specific one, simply because its type MoreSpecial is a strict subtype of TS.

Moreover,

  • if you uncomment the b line, it gives ambiguous implicits error (b: b.type <: TS conflicts with s: MoreSpecial <: TS)
  • if you comment the s line, it also gives ambiguous implicits error (a: TS conflicts with f: TS)
  • only if ((s is commented) XOR (b is commented)), then it compiles (both b: b.type and s: MoreSpecial win over a: TS and f: TS)

That's all as expected. This holds for 2.12.6, so it doesn't seem specific to 2.13-Mx.

Sign up to request clarification or add additional context in comments.

6 Comments

Nice, basically static overloading rules in play. Good catch, thanks!
Great explanation. Is there a motivation for preferring narrower types?
@Owen Afair, the language designers needed some way to order the implicits from less specific to more specific. Giving a value with the widest supertype of the type for which you're currently searching an implicit value doesn't seem to make any sense: an implicit Any defined anywhere would beat everything else. So they took the subtyping relation. This choice sometimes leads to counter-intuitive behavior when combined with variance annotations, though...
@AndreyTyukin Well, an implicit Any would likely not satisfy the implicit bounds. An implicit Nothing would, though, and it would also beat everything under the narrowest rule...
@Owen yeah, I guess the reasoning went somehow along those lines: a value of type Any would not be substitutable where a value of some other type T is needed... I don't remember the exact arguments for and against every proposed notion of "specificity", if you want, you can google for "scala, contravariance, implicits", and you'll probably find quite a few heated debates from few years ago. It seems, later this whole problem area has been deemphasized, and people simply found some other topics to disagree on...
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.