Don't include all parts of type of implicit value. Implicits are plenty magical in how they propagate, let's make it tractable to reason about where they come from, while supporting common use cases of companion objects supplying e.g. serializers for the type they accompany.
Could you sketch out the rules for the implicit scope you have in mind? If I understand what you're proposing correctly, this seems overly restrictive. The implicit scope of the type Foo[Bla] should include both Foo and Bla, otherwise it's either impossible to define a new typeclass that has instances for existing data structures, or impossible to define a new data structure that has an instance of an existing typeclass.
I agree with the sentiment to make it tractable to reason about where implicit values come from, but am not yet sure that reducing implicit scope is the best way to accomplish it. If this ticket were titled “Make implicit scope easier to reason about” would the solution still be to narrow implicit scope? What about making it easier to visualize implicit scope, through printing of an implicit scope graph maybe? Or the ability to interact with the scope at debug time and find objects available and their source?
Can someone name a specific search location that is unnecessary?
For example, the various subpackages that prefix any type that's part of the type of the implicit we're looking for? See the recursive computation of the parts of a type specified by http://www.scala-lang.org/files/archive/spec/2.12/07-implicits.html#implicit-parameters
That's a lot of parts! :-)
For the record, I don't have a concrete proposal, or I would be SIPping it ;-)
Prefixes definitely seem like a candidate for pruning. I think most people are unaware that they form part of the implicit scope, and I think there are very few examples in the wild of this being directly exploited (cue someone finding an example of me doing this in shapeless ;-).
Prefixes are probably safe to remove. I can't think of any case where it would be considerably more difficult to achieve the same result without them. Usually the required changes should be limited to moving code around. There is a possibility of having a corner case where you want the same implicits to be available in multiple scopes (so they are currently scoped in a prefix) but you cannot duplicate them because that would lead to ambiguities when the scopes overlap. This could possibly be solved by treating implicits from multiple paths as identical in certain cases (e.g. simple aliasing of objects).
A more restrictive alternative would be to only consider type variables which have an explicit annotation that makes them part of an implicit scope. For Foo[Bar]] to add Bar to the implicit scope Foo would have to be defined as something like class Foo[T @implicitScope].
Maybe we should widen the ticket beyond narrowing implicit scope to simplifying implicit search.
I agree that package objects in prefixes would be a good candidate for removal. For other prefixes there might be surprises, though. E.g.
trait ADT
object ADT {
class Left extends ADT
class Right extends ADT
implicit def leftIsSerializable: Serializable[Left]
}
Seems reasonable, but the implicit leftIsSerializable would no longer be in the implicit scope of Left.
Seems reasonable, but the implicit
leftIsSerializablewould no longer be in the implicit scope of Left.
In that particular example, it would still be in scope based on the rule that includes companion implicits of all base types of Left.
In that particular example, it would still be in scope based on the rule that includes companion implicits of all base types of Left.
Ah yes, you are right. In that case, maybe we could experiment with dropping the prefix rule and find out how much breaks?
We put implicits on package objects in the ensime api, to save boilerplate, but it shouldn't be too hard to refactor it into an import. It's probably a code smell anyway, because it breaks typeclass coherence.
Most helpful comment
Prefixes definitely seem like a candidate for pruning. I think most people are unaware that they form part of the implicit scope, and I think there are very few examples in the wild of this being directly exploited (cue someone finding an example of me doing this in shapeless ;-).