This page is no longer maintained — Please continue to the home page at www.scala-lang.org

the shadow knows

12 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

(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.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: the shadow knows

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] ^

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: the shadow knows
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

On Wed, Dec 7, 2011 at 1:29 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
(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.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: the shadow knows

On Wed, Dec 7, 2011 at 17:00, Rex Kerr 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.

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: the shadow knows


On Wed, Dec 7, 2011 at 2:23 PM, Daniel Sobral <dcsobral [at] gmail [dot] 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

Derek Williams 3
Joined: 2011-08-12,
User offline. Last seen 42 years 45 weeks ago.
Re: the shadow knows
On Wed, Dec 7, 2011 at 12:23 PM, Daniel Sobral <dcsobral [at] gmail [dot] 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
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: the shadow knows

On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr 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!

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: the shadow knows

On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr wrote:
>
>
> On Wed, Dec 7, 2011 at 2:23 PM, Daniel Sobral 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!

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: the shadow knows

On Thu, Dec 8, 2011 at 00:47, martin odersky wrote:
> On Wed, Dec 7, 2011 at 11:32 AM, Rex Kerr wrote:
>>
>>
>> On Wed, Dec 7, 2011 at 2:23 PM, Daniel Sobral 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

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#keywordsre....
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.

tolsen77
Joined: 2008-10-08,
User offline. Last seen 1 year 38 weeks ago.
Re: the shadow knows
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 }


On 7 December 2011 19:29, Paul Phillips <paulp [at] improving [dot] org> wrote:
(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.

Lars Hupel
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
Re: the shadow knows

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.

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: the shadow knows

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.

On 10 dec, 00:59, Lars Hupel wrote:
> 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.

Lars Hupel
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
Re: the shadow knows

> 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.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland