0
votes

The following is a simple example that tests the implicit feature of the scala 2.13 compiler:

object OverridingScope {

  trait System {

    trait Handler[T]
  }

  object Sys1 extends System {

    object Handler {

      implicit def handle1: Handler[Int] = new Handler[Int] {}
    }

    implicit def handle2: Handler[Long] = new Handler[Long] {}
  }
}

According this article:

Where does Scala look for implicits?

The companion object of the trait Handler can be automatically imported as part of the implicit scope when type Handler[_] is used, the outer object Sys1 should be irrelevant. This means handle1 should be visible and handle2 should be hidden.

But the following test case shows the exact opposite:

class OverridingScope extends BaseSpec {

  import OverridingScope._

  it("implicits in companion is in scope") {

//    val hh = implicitly[Sys1.Handler[Int]]
    // Doesn't work
  }

  it("implicits in outer object is in scope") {

    val hh = implicitly[Sys1.Handler[Long]]
    // Should NOT work
  }
}

Considering that Handler is both a class & object name, the fact that handle1 doesn't work looks highly suspicious. Also, I've checked the full list of implicit import:

First look in current scope
Implicits defined in current scope
Explicit imports
wildcard imports
Same scope in other files
Now look at associated types in
Companion objects of a type
Implicit scope of an argument's type (2.9.1)
Implicit scope of type arguments (2.8.0)
Outer objects for nested types
Other dimensions

None of the above can explain why handle2 is in the scope. What could be the reasons?

BTW: For those who dismissed implicit as a important scala feature: Implicit is the only way to implement type class, and if you want to implement it with minimal boilerplate, sometimes the above pattern can be the only choice.

1
object Handler does not appear to be the companion object of trait Handler[T].jwvh
More precisely, it isn't the companion object because it's defined in a different scope and the specification says: "Generally, a companion module of a class is an object which has the same name as the class and is defined in the same scope and compilation unit. Conversely, the class is called the companion class of the module."Alexey Romanov
"None of the above can explain why handle2 is in the scope" Why do you think "Outer objects for nested types" doesn't explain it?Alexey Romanov

1 Answers

5
votes

Point 3 here explains that implicits/givens in prefixes of the type in question contribute to its implicit scope (in Scala 3 not if it's a package). So for the type Sys1.Handler[Long] implicits in Sys1 are in the implicit scope because Sys1 is a non-package prefix.

handle1 is not in the implicit scope because object Handler in object Sys1 is not the companion object of trait Handler in trait System.