Search code examples
scalaresolutionimplicitscala-2.13

Why implicit object has precedence over val in Scala 2.13-M5?


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]

Solution

  • 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.