bug
bug copied to clipboard
Type error when outer scope name is shadowed by inheritance
reproduction steps
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_181).
Type in expressions for evaluation. Or try :help.
scala> def lazyMap[T, U](coll: Iterable[T], f: T => U) =
| new Iterable[U] {
| def iterator = coll.iterator map f
| }
problem
scala> def lazyMap[T, U](coll: Iterable[T], f: T => U) =
| new Iterable[U] {
| def iterator = coll.iterator map f
| }
def iterator = coll.iterator map f
^
On line 3: error: type mismatch;
found : T => U
required: U => U
expectation
This code is from book Programming in Scala 3rd Edition. Section 24.14 Views
The type of coll should be Iterable[T] but complier judge it as Iterable[U] because the coll is as the same name the function coll defined in trait Iterable
In src/library/scala/collection/Iterable.scala
trait Iterable[+A] extends IterableOnce[A]
with IterableOps[A, Iterable, Iterable[A]]
with IterableFactoryDefaults[A, Iterable] {
// The collection itself
final def toIterable: this.type = this
final protected def coll: this.type = this
....
I'm new to Scala, I'm not sure here is design as a feature or not.
One workaroud is rename the parameter
scala> def lazyMap[T, U](coll: Iterable[T], f: T => U) = {
| val _coll = coll
| new Iterable[U] {
| def iterator = _coll.iterator.map(f)
| }
| }
lazyMap: [T, U](coll: Iterable[T], f: T => U)Iterable[U]
Consider the inner variable socpe will cover the outer ones, should add some hint here? Because a trait may have a lot of the function or variable, check variable inside defined trait one by one may not be so easy.
How about adding some hints like this?
scala> def lazyMap[T, U](coll: Iterable[T], f: T => U) =
| new Iterable[U] {
| def iterator = coll.iterator map f
| }
def lazyMap[T, U](coll: Iterable[T], f: T => U) =
^
On line 1: warning: variable defined in inner scope
"coll" has defined in triat "Iterable", may be you need to rename it.
def iterator = coll.iterator map f
^
On line 3: error: type mismatch;
found : T => U
required: U => U
Wow, that's really unfortunate and unintuitive, IMHO! Thanks for the report.
About the warning suggestion, my gut feeling is that emitting a warning like that will cause a lot of false positives (i.e. cases where the external name is shadowed, but that isn't a problem).
But I wonder if this behaviour has a history. I've seen @odersky semi-recently argue in similar-ish situations (to do with import scopes and having code in 1 file or split into 2 files) something along the lines of "seen things should be seen". So, here, shouldn't the coll: Iterable[T] win over the inherited coll: Iterable[U]? 🤔
That's indeed unfortunate. An immediate fix is to rename the coll in Iterable, which is right now defined like this:
final protected def coll: this.type = this
It would be good to use a less common name for this.
About the problem in general: Maybe we should experiment with a warning or even an error. There is some precedence that a local import does not shadow an outer declaration. Example:
object a:
def f = 1
object b:
def f = 1
object c:
import a._
f
Here you get an ambiguity error.
7 | f
| ^
| Reference to f is ambiguous
| it is both defined in object b
| and imported subsequently by import a._
The motivation for this is that we do not want a "less explicit" construct (the import) shadow a "more explicit" construct (the declaration). Analogously, we might not want to allow an inherited name to shadow an explicitly declared name in an outer scope.
The PR here just takes all "foreign definitions" at precedence level 4, so that an inherited member defined elsewhere is like a package member defined elsewhere.
The PR will be updated to align with dotty under -Xsource:3. JLS has an illustrative example at 6.5.7.1 (“Simple method names”).
Opened a new PR because of the pushing to a closed PR means github won't allow reopening even though it makes no sense because computers.