Our friend the SLS tells us that "A binding in some inner scope shadows bindings of lower precedence in the same scope as well as bindings of the same or lower precedence in outer scopes." (Ch. 2). Testing scalac behavior suggests that explicit imports have lower precedence than definitions in the same package but in other compilation units; if that were the case imports couldn't shadow definitions. That doesn't sound right, does it? A simple example will show that the spec and scalac don't agree here. (Or, possibly, the language of the spec is just confusing, but that should still be fixed).
Should this seems theoretical, this issue came up while fixing a compilation error in SBT, which only appears within Eclipse (and not when compiling with SBT) - the reason for the inconsistency seems yet different.
Here's the example:
File1.scala:
package P {
object X { val x = 1; val y = 2 }
}
File2.scala:
package Q {
object X { val x = true; val y = "" }
}
Test.scala:
package P { // ‘X’ bound by package clause
import Q.X //<<<
object A {
println("L4: "+X) // ‘X’ refers to ‘P.X’ here
}
}
Result:
[warn] /Users/pgiarrusso/Documents/Research/Sorgenti-Sync/Scala/importSpecificity/src/main/scala/Test.scala:2: imported `X' is permanently hidden by definition of object X in package P
[warn] import Q.X
[warn] ^
[warn] one warning found
So, it seems that P.X, even though declared elsewhere, has higher priority. Now let's try to import Q.X in an inner scope. Since an explicit import has lower priority, according to the spec this should cause an ambiguity error, and make it impossible to import any X within package P. Pretty bad, uh? Luckily, not the case.
package P { // ‘X’ bound by package clause
object A {
import Q.X //import Q._ also works.
println("L4: "+X) // ‘X’ refers to ‘Q.X’ here
}
}
scala> P.A
L4: Q.X$@4f388e
res0: P.A.type = P.A$@5109f2ca
As you see, the code compiles, and P.A is a Q.X. Following the spec, we can only conclude that P imports X with priority lower than wildcard imports, contradicting our earlier contradiction. How's that possible? And especially, why don't I just look up the priority of the bindings to package members?
- Definitions and declarations that are [...] or made available by a package clause in the same compilation unit where the definition occurs have highest precedence.
- Explicit imports have next highest precedence.
- Wildcard imports have next highest precedence.
- Definitions made available by a package clause not in the compilation unit where the definition occurs have lowest precedence.
I'm a bit confused about the difference between 1 and 4, and that's why I tried to find out practically what happens. My interpretation is as follows.
When we compile Test.scala and we process the "package P" declaration, P.X should enter in scope. Since X is defined in another compilation unit (File1.scala), rule 4 should always apply, and P.X should have lowest priority. In the second case, this matches practice, and this is good. However, in the first case, P.X behaves as if it had highest priority - which seems clearly inconsistent. This also seems undesirable to me - why should an explicit import be ignored?
Overall, what do you think? Should I file a ticket?
Cheers,
--
Paolo G. Giarrusso - Ph.D. Student, Philipps-University Marburg
http://www.informatik.uni-marburg.de/~pgiarrusso/