the shadow knows

493 views
Skip to first unread message

Paul Phillips

unread,
Dec 7, 2011, 6:29:41 PM12/7/11
to scala-l...@googlegroups.com
(Actually the shadow does not know, that's the problem.)

I'm going to take one last swing at this and then wash my hands of it
forever. If I'm the only guy who thinks this is wrong, well it won't
be the first time that's happened. Your silence will tell me all I
need. But if anybody ELSE thinks this is wrong, then maybe you'd like
to take the ball a while because I just washed my hands and that ball
is grimy.

class A(var bippy: Int) { def f1 = "A.bippy = " + bippy }
class B(bippy: Int) extends A(bippy) { def f2 = "B.bippy = " + bippy }
class C(dingus: Int) extends B(dingus) { def f3 = "C.bippy = " + bippy }

val c = new C(10)
List(c.f1, c.f2, c.f3) foreach println
c.bippy = 123123
List(c.f1, c.f2, c.f3) foreach println

Do you know what this prints? Is there any question in your mind? Here
is what it prints.

A.bippy = 10
B.bippy = 10
C.bippy = 10
A.bippy = 123123
B.bippy = 10
C.bippy = 123123

That's it, I'm done. The ticket is
https://issues.scala-lang.org/browse/SI-4762 if anyone wants a piece
of it.

Paul Phillips

unread,
Dec 7, 2011, 6:38:23 PM12/7/11
to scala-l...@googlegroups.com
Sorry, I left out one thing. In case anyone thinks this is an
academic issue which doesn't come up.


[scalacfork] Compiling 46 files to /scratch/trunk1/build/quick/classes/library
[scalacfork] /scratch/trunk1/src/actors/scala/actors/ActorTask.scala:35:
warning: private[this] value msg in class ActorTask shadows mutable
msg inherited from class ReactorTask. Changes to msg will not be
visible within class ActorTask - you may want to give them distinct
names.
[scalacfork] if (msg != null)
Some(msg) else None,
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/actors/scala/actors/ActorTask.scala:35:
warning: private[this] value msg in class ActorTask shadows mutable
msg inherited from class ReactorTask. Changes to msg will not be
visible within class ActorTask - you may want to give them distinct
names.
[scalacfork] if (msg != null)
Some(msg) else None,
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/actors/scala/actors/ReplyReactorTask.scala:26:
warning: private[this] value reactor in class ReplyReactorTask shadows
mutable reactor inherited from class ReactorTask. Changes to reactor
will not be visible within class ReplyReactorTask - you may want to
give them distinct names.
[scalacfork] Actor.tl set reactor
[scalacfork] ^
[scalacfork] three warnings found

[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:116:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] val tag = bytes(index(i)).toInt
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:123:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] val tag = bytes(index(i))
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:129:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] val tag = bytes(index(i)).toInt
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:135:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] val tag = bytes(index(i)).toInt
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:141:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] val tag = bytes(index(i)).toInt
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:182:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] case TERMname => newTermName(bytes, readIndex, len)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:183:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] case TYPEname => newTypeName(bytes, readIndex, len)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:440:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] protected def readAnnotArg(i: Int): Tree =
bytes(index(i)) match {
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/reflect/internal/pickling/UnPickler.scala:454:
warning: private[this] value bytes in class Scan shadows mutable bytes
inherited from class PickleBuffer. Changes to bytes will not be
visible within class Scan - you may want to give them distinct names.
[scalacfork] protected def readClassfileAnnotArg(i: Int):
ClassfileAnnotArg = bytes(index(i)) match {
[scalacfork]
^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/interactive/Global.scala:444:
warning: private[this] value reporter in class Global shadows mutable
reporter inherited from class Global. Changes to reporter will not be
visible within class Global - you may want to give them distinct
names.
[scalacfork] reporter.reset()
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/interactive/Global.scala:493:
warning: private[this] value reporter in class Global shadows mutable
reporter inherited from class Global. Changes to reporter will not be
visible within class Global - you may want to give them distinct
names.
[scalacfork] reporter.error(unit.body.pos, "Presentation
compiler crashed while type checking this file:
%s".format(ex.toString()))
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/interactive/Global.scala:543:
warning: private[this] value reporter in class Global shadows mutable
reporter inherited from class Global. Changes to reporter will not be
visible within class Global - you may want to give them distinct
names.
[scalacfork] if (debugIDE && !reporter.hasErrors)
validatePositions(unit.body)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/transform/Erasure.scala:603:
warning: private[this] value context in class Eraser shadows mutable
context inherited from class Typer. Changes to context will not be
visible within class Eraser - you may want to give them distinct
names.
[scalacfork] Console.println(er.msg + " in file " +
context.owner.sourceFile)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala:82:
warning: private[this] value context in class BodyDuplicator shadows
mutable context inherited from class Typer. Changes to context will
not be visible within class BodyDuplicator - you may want to give them
distinct names.
[scalacfork] val sym1 = context.scope.lookup(sym.name)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala:147:
warning: private[this] value context in class BodyDuplicator shadows
mutable context inherited from class Typer. Changes to context will
not be visible within class BodyDuplicator - you may want to give them
distinct names.
[scalacfork] val newsym = ldef.symbol.cloneSymbol(context.owner)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala:155:
warning: private[this] value context in class BodyDuplicator shadows
mutable context inherited from class Typer. Changes to context will
not be visible within class BodyDuplicator - you may want to give them
distinct names.
[scalacfork] val newsym = vdef.symbol.cloneSymbol(context.owner)
[scalacfork] ^
[scalacfork] /scratch/trunk1/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala:184:
warning: private[this] value context in class BodyDuplicator shadows
mutable context inherited from class Typer. Changes to context will
not be visible within class BodyDuplicator - you may want to give them
distinct names.
[scalacfork] enterSym(context, ddef)
[scalacfork] ^

Rex Kerr

unread,
Dec 7, 2011, 7:00:49 PM12/7/11
to scala-l...@googlegroups.com
Despite knowing what the answer is, I agree that this is highly unfortunate behavior.  Shadowing vars with vals should be at least a warning if not an error unless annotated (e.g. @shadow).  Likewise if it's a def x/def x_= pair.  In fact, I'm not sure it's okay to shadow _anything_ without saying so explicitly (though vals are the least problematic).  For example, with side effects:

  class A(foo: Int) { def bar = { println("Hi"); foo } }
  class B(bar: Int) extends A(bar*2) { def baz = bar + bar }
  class C extends B(2) { def result = baz*bar }  // How many times do I print "Hi"?

In such a short example, it's not as maddening as yours, but it could be a real problem in anything longer.

  --Rex

Daniel Sobral

unread,
Dec 7, 2011, 7:23:32 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 17:00, Rex Kerr <ich...@gmail.com> wrote:
> Despite knowing what the answer is, I agree that this is highly unfortunate
> behavior.  Shadowing vars with vals should be at least a warning if not an
> error unless annotated (e.g. @shadow).  Likewise if it's a def x/def x_=
> pair.  In fact, I'm not sure it's okay to shadow _anything_ without saying
> so explicitly (though vals are the least problematic).  For example, with
> side effects:
>
>   class A(foo: Int) { def bar = { println("Hi"); foo } }
>   class B(bar: Int) extends A(bar*2) { def baz = bar + bar }
>   class C extends B(2) { def result = baz*bar }  // How many times do I
> print "Hi"?
>
> In such a short example, it's not as maddening as yours, but it could be a
> real problem in anything longer.

Same thing. I knew the answer, but this is one of the most common
complains I heard. Some workaround suggestions are:

class B(bippy0: Int) extends A(bippy0) { def f2 = "B.bippy = " + bippy }
class B(_bippy: Int) extends A(_bippy) { def f2 = "B.bippy = " + bippy }

Honestly, the lameness of the workaround is in itself a strong
indicator of how awful this particular interaction of features is. Not
that I have any suggestions on how to handle it, besides a warning.

--
Daniel C. Sobral

I travel to the future all the time.

Derek Williams

unread,
Dec 7, 2011, 7:31:34 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 12:23 PM, Daniel Sobral <dcso...@gmail.com> wrote:
Honestly, the lameness of the workaround is in itself a strong
indicator of how awful this particular interaction of features is. Not
that I have any suggestions on how to handle it, besides a warning.

Another workaround:

class B(bippy: Int) extends A(bippy) { def f2 = "B.bippy = " + (this: A).bippy }

--
Derek Williams

Rex Kerr

unread,
Dec 7, 2011, 7:32:45 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 2:23 PM, Daniel Sobral <dcso...@gmail.com> wrote:

Same thing. I knew the answer, but this is one of the most common
complains I heard. Some workaround suggestions are:

 class B(bippy0: Int) extends A(bippy0)   { def f2 = "B.bippy = " + bippy }
 class B(_bippy: Int) extends A(_bippy)   { def f2 = "B.bippy = " + bippy }

Honestly, the lameness of the workaround is in itself a strong
indicator of how awful this particular interaction of features is. Not
that I have any suggestions on how to handle it, besides a warning.

How about

  class B(_) extends A(_) { def f2 = ... }

I don't know why I should have to bother repeating the type information if all I want to do is forward parameters to A's constructor.

--Rex

Paul Phillips

unread,
Dec 7, 2011, 7:40:17 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr <ich...@gmail.com> wrote:
> I don't know why I should have to bother repeating the type information if
> all I want to do is forward parameters to A's constructor.

Because A could have more than one one-argument constructor.
Auxiliaries are second-class enough without syntactically excluding
them from consideration. Still, something in the spirit of this idea
would sure be nice. I hate having to mkae up names for batons I'm
passing between relay racers. They're batons! They're all just
batons! Stop trying to humanize them!

Rex Kerr

unread,
Dec 7, 2011, 7:46:39 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 2:40 PM, Paul Phillips <pa...@improving.org> wrote:
On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr <ich...@gmail.com> wrote:
> I don't know why I should have to bother repeating the type information if
> all I want to do is forward parameters to A's constructor.

Because A could have more than one one-argument constructor.

That is a problem because...?

    class B(_,_,_) extends A(_,_,_,"Is this not perfectly clear?")
    class B(_,_) extends A("You get",_)("the idea, yes?",_)

--Rex


 

Paul Phillips

unread,
Dec 7, 2011, 8:02:57 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 11:46 AM, Rex Kerr <ich...@gmail.com> wrote:
> That is a problem because...?
>
>     class B(_,_,_) extends A(_,_,_,"Is this not perfectly clear?")
>     class B(_,_) extends A("You get",_)("the idea, yes?",_)

class A(x: Int) {
def this(x: String) = this(5)
def this(x: List[Int]) = this(x.head)
}
class B(_) extends A(_)

What's that?

Paul Phillips

unread,
Dec 7, 2011, 8:05:24 PM12/7/11
to scala-l...@googlegroups.com
I didn't get the impression you had this in mind, but we could forward
all the auxiliaries which matched, which would actually be kind of
awesome because there's absolutely no way to do that at present. You
subclass something with five auxiliary constructors, you can write all
five of those constructors again and you can like it.

Michael Schmitz

unread,
Dec 7, 2011, 8:11:32 PM12/7/11
to scala-l...@googlegroups.com
Or you could

class B(_: String) extends A(_)

Rex Kerr

unread,
Dec 7, 2011, 8:29:48 PM12/7/11
to scala-l...@googlegroups.com
That would be awesome indeed.  My answer was going to be "Choose the primary constructor, of course, unless it has the wrong number or type of arguments"--the reason being that the primary contstructor is where vals/vars must be declared, and matching that type is most likely to lead to unintentional shadowing.

But copying all of them would be far more awesome.  And possibly even a good idea.

  --Rex

Paul Phillips

unread,
Dec 7, 2011, 8:36:30 PM12/7/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 12:29 PM, Rex Kerr <ich...@gmail.com> wrote:
> That would be awesome indeed.  My answer was going to be "Choose the primary
> constructor, of course,

I did anticipate that answer, which is why I wrote "Auxiliaries are


second-class enough without syntactically excluding them from

consideration" to start. But it's not so bad, maybe we bumbled our
way toward a good idea.

martin odersky

unread,
Dec 8, 2011, 2:47:27 AM12/8/11
to scala-l...@googlegroups.com
On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr <ich...@gmail.com> wrote:
>
>
> On Wed, Dec 7, 2011 at 2:23 PM, Daniel Sobral <dcso...@gmail.com> wrote:
>>
>>
>> Same thing. I knew the answer, but this is one of the most common
>> complains I heard. Some workaround suggestions are:
>>
>>  class B(bippy0: Int) extends A(bippy0)   { def f2 = "B.bippy = " + bippy
>> }
>>  class B(_bippy: Int) extends A(_bippy)   { def f2 = "B.bippy = " + bippy
>> }
>>
>> Honestly, the lameness of the workaround is in itself a strong
>> indicator of how awful this particular interaction of features is. Not
>> that I have any suggestions on how to handle it, besides a warning.
>
>
> How about
>
>   class B(_) extends A(_) { def f2 = ... }
>
Please. Not another different use for the underscore!

-- Martin

Daniel Sobral

unread,
Dec 8, 2011, 11:50:41 AM12/8/11
to scala-l...@googlegroups.com

LOL!

Actually, I'm much more comfortable with this one than with these:

f[A[_] <: B[_]]
import p.{x => _, _}
f(_, _ + 1)

But I'll grant that there's some ambiguity here:

class B(_) extends A(_)
x match { B(_) => ... }
f(_)

They do look too much alike. I do wish the repeated constructor
parameters problem was solved, though, and the proposed syntax does
have a certain elegance. And, for better or worse, Scala does use
underscore as the wildcard syntax for not creating new keywords. I
invite anyone who thinks I'm exaggerating to look at the second table
here: http://docs.scala-lang.org/tutorials/FAQ/finding-symbols.html#keywordsreserved_symbols.
Or just ponder the meaning of each underscore in the three examples I
gave.

By the way, I've come to the conclusion that hiding keywords behind
underscore is a lie. The keyword count might be decreased, but at the
cost of people needing to disambiguate underscore on their minds.

Trond Olsen

unread,
Dec 8, 2011, 6:22:54 PM12/8/11
to scala-l...@googlegroups.com
I remember registering a complaint in 2009 concerning shadowing. I was a bit surprised that constructor arguments are promoted to variables if you referenced the name, not in the class initialization code, but in method body.


scala> class A(var name: String)
defined class A


scala> class B(name: String) extends A(name) {
     |   def rename(name: String) { this.name = name }
     | }
<console>:9: error: reassignment to val
         def rename(name: String) { this.name = name }

Lars Hupel

unread,
Dec 9, 2011, 11:59:31 PM12/9/11
to scala-l...@googlegroups.com
It suddenly occurred to me that `extends` in class definitions is
similar to `=` in method definitions:

class A(n: Int)
class B(n: Int) extends A(n+1)

def a(n: Int) = 0
def b(n: Int) = a(n+1)

By analogy, we can construct from this:

def b = a _
// or `a(_)`

that:

class B extends A _

This however lead me to the bold thought that `extends` could be dropped
completely, such that we could write the following:

class B = A _ {
// ...
}

IMHO, allowing `_` in class definitions would even increase consistency
in the language.

Dave

unread,
Dec 10, 2011, 12:28:07 AM12/10/11
to scala-language
I think it is basically calling super(...) in the constructor
Isn't it possible to use super in class constructor as keyword for the
pass-through variables and remove the redundant parameters after
extends like so:

class A(var bippy: Int) { def f1 = "A.bippy = " + bippy }

class B(super bippy: Int) extends A { def f2 = "B.bippy = " + bippy }
class C(super dingus: Int) extends B { def f3 = "C.bippy = " + bippy }

or

class A(var bippy: Int) { def f1 = "A.bippy = " + bippy }

class B(super(bippy: Int)) extends A { def f2 = "B.bippy = " + bippy }
class C(super(dingus: Int)) extends B { def f3 = "C.bippy = " +
bippy }

With the second variation you can use the one super keyword for more
parameters

Or allow both styles

I think it is conciser and more clear what happens.

Lars Hupel

unread,
Dec 10, 2011, 4:49:25 PM12/10/11
to scala-l...@googlegroups.com
> class A(var bippy: Int) { def f1 = "A.bippy = " + bippy }
> class B(super bippy: Int) extends A { def f2 = "B.bippy = " + bippy }
> class C(super dingus: Int) extends B { def f3 = "C.bippy = " + bippy }
>
> or
>
> class A(var bippy: Int) { def f1 = "A.bippy = " + bippy }
> class B(super(bippy: Int)) extends A { def f2 = "B.bippy = " + bippy }
> class C(super(dingus: Int)) extends B { def f3 = "C.bippy = " +
> bippy }

To be honest, nothing of that appeals to me. The former syntax makes
somewhat sense, although `super` is not really a "modifier" for the
parameter (as `implicit` would be). The latter however looks artificial
as it required completely new syntax.

Reply all
Reply to author
Forward
0 new messages