Interesting Scoping Rules

C

Charles Comstock

val = "fem"
class Test
puts val
end

# or

class Test
val = "A"
def info
puts val
end
end

Test.new.info

# or

val = 1
def t
puts val
end
t

Why is it none of these are allowed? All of these seem like perfectly
logical places that lexical scoping would allow. It really suprised me
that none of thse work, anyone have an explanation for this behavior?

Charles Comstock
 
D

David A. Black

Hi --

val = "fem"
class Test
puts val
end

# or

class Test
val = "A"
def info
puts val
end
end

Test.new.info

# or

val = 1
def t
puts val
end
t

Why is it none of these are allowed? All of these seem like perfectly
logical places that lexical scoping would allow. It really suprised me
that none of thse work, anyone have an explanation for this behavior?

My explanation is kind of circular, I guess: those variables are out
of scope because def starts a new scope :) I'm so used to it I don't
really notice it. I don't have any real technical insight into why
it's that way.

But I just wanted to add... don't forget you can use #define_method if
you want to keep the same scope:

class A
a = 1
define_method("b", lambda { puts a })
end

A.new.b # 1


David
 
D

Dick Davies

* Charles Comstock <[email protected]> [0424 01:24]:

Take this with a pinch of salt, I am on my first cup of caffeine
- I'm sure I'll get corrected if it's glaringly wrong. (I hope so)
val = "fem"

This defines a local variable called 'val' on Object
- not the same as an instance variable.
class Test
puts val
end

Test can't see the local variable on Object, you get an error like:

in `t': undefined local variable or method `val' for #<Object:0x807bcd8> (NameError)
from aaa:5

So you'd need an accessor on Object to get to it, and even then I think it's gone
out of scope.
(This of course won't print anything until you call Test.new either).

*this* works:

class Test
val = "fem"
puts val
end
Test.new
# or

class Test
val = "A"
def info
puts val
end
end

Test.new.info

you can't see val here because it's not an instance variable of Test
objects, it's a local variable of the Class Test. Even if you make it an
instance variable, and call it directly then
you have problems, because there is no accessor method defined:

rasputin@lb:tmp$ cat bbb.rb
class Test
@val = "A"
def info
puts Test.val
end
end
Test.new.info
rasputin@lb:tmp$ ruby -w bbb.rb
bbb.rb:4:in `info': undefined method `val' for Test:Class (NameError)
from bbb.rb:7

# or

val = 1
def t
puts val
end
t

I think this is because val is a local variable, not an instance variable of
Object. Again, it's gone out of scope by the time you call t().
If you replace val with @val, it prints '1', which is what you'd probably expect.
 
C

Charles Comstock

Dick said:
* Charles Comstock <[email protected]> [0424 01:24]:

Take this with a pinch of salt, I am on my first cup of caffeine
- I'm sure I'll get corrected if it's glaringly wrong. (I hope so)

val = "fem"


This defines a local variable called 'val' on Object
- not the same as an instance variable.

class Test
puts val
end


Test can't see the local variable on Object, you get an error like:

in `t': undefined local variable or method `val' for #<Object:0x807bcd8> (NameError)
from aaa:5

So you'd need an accessor on Object to get to it, and even then I think it's gone
out of scope.
(This of course won't print anything until you call Test.new either).

It's not so much that it hasn't gone out of scope here, so much as it is
no longer accessable from the inner scope. If I use val further on
after the class def it will work perfectly fine.
*this* works:

class Test
val = "fem"
puts val
end
Test.new




you can't see val here because it's not an instance variable of Test
objects, it's a local variable of the Class Test. Even if you make it an
instance variable, and call it directly then
you have problems, because there is no accessor method defined:

rasputin@lb:tmp$ cat bbb.rb
class Test
@val = "A"
def info
puts Test.val
end
end
Test.new.info
rasputin@lb:tmp$ ruby -w bbb.rb
bbb.rb:4:in `info': undefined method `val' for Test:Class (NameError)
from bbb.rb:7





I think this is because val is a local variable, not an instance variable of
Object. Again, it's gone out of scope by the time you call t().
If you replace val with @val, it prints '1', which is what you'd probably expect.

I guess I should have qualified my statement a little better, I did
recognize that a def seems to be a hard scoping boundary, the question
was much more that I was very suprised by this, and was curious if
anyone had any insight as to WHY ruby chose to work things like this?

It suprises me much in the same way that we cannot def inside a def, it
will result in a new def at the top scope. It also suprises me in the
way that if we stick a class inside of a class instance variables will
not bubble down into the inner class.

Was this all just a sorta clever way to avoid needing to back check up
all scopes each time a variable is used?

It just goes against my general programming language intuition, it feels
singularly un-schemeish in a place I expected it to act scheme-ish, let
alone java-ish, c++ish and most other programming languages I can think
of. I'm sure I'll get over it as it does most other things the way I
like, but it quite suprised me.

Charles Comstock
 
D

David A. Black

Hi --

I guess I should have qualified my statement a little better, I did
recognize that a def seems to be a hard scoping boundary, the question
was much more that I was very suprised by this, and was curious if
anyone had any insight as to WHY ruby chose to work things like this?

I think it's generally true that local variable scope changes every
time a new 'self' takes over:

class X
# self is X
def blah
# self is any instance of X
end
# self is X again, back to where we were
end

class X
# self is X but we've backed out of the class and
# come back in, so we have a new local scope
end

The design is flexible: you can use #define_method instead of def, if
you want to avoid changing self and therefore starting a new local
scope.
It suprises me much in the same way that we cannot def inside a def, it
will result in a new def at the top scope. It also suprises me in the
way that if we stick a class inside of a class instance variables will
not bubble down into the inner class.

I'm not sure what you mean by that last one -- can you illustrate?


David
 
C

Charles Comstock

David said:
Hi --




I think it's generally true that local variable scope changes every
time a new 'self' takes over:

class X
# self is X
def blah
# self is any instance of X
end
# self is X again, back to where we were
end

class X
# self is X but we've backed out of the class and
# come back in, so we have a new local scope
end

The design is flexible: you can use #define_method instead of def, if
you want to avoid changing self and therefore starting a new local
scope.




I'm not sure what you mean by that last one -- can you illustrate?


David

class A
var = "me"
class B
puts var
end
end

It's essentially the same rule that causes

var = "me"
class B
puts var
end

alone not to work I suppose. The other complaint was that

def a
def b
puts "a"
end
end

b

will result in
"a"

as opposed to getting a name error on b

also curious is that

class A
@a = "me"
class B
puts @a
end
end

results in
nil

again class appears to be a much harder scoping boundary
then I expected. Or rather that class b is really class A::B
and as such has no knowledge of ancestory to A.

class A
@a = "me"
class B
def t
puts @a
end
end
end

A::B.new.t

this is suggested by this returning nil.

Charles Comstock
 
T

ts

C> also curious is that

try this :)

C> class A
C> @a = "me"
C> class B
C> puts @a
C> end
C> end

class A
C = "C"
class B
puts C
end
end


Guy Decoux
 
C

Charles Comstock

ts said:
C> also curious is that

try this :)

C> class A
C> @a = "me"
C> class B
C> puts @a
C> end
C> end

class A
C = "C"
class B
puts C
end
end


Guy Decoux

Right but the whole reasoning for doing something like that would be to
effect it later. This is wrong though in my opinion, if local vars get
lost in a change of class/def then why should constants go through these
barriers. Anyone else have thoughts on this?

Charlie
 
D

Dan Doel

also curious is that

class A
@a = "me"
class B
puts @a
end
end

results in
nil

again class appears to be a much harder scoping boundary
then I expected. Or rather that class b is really class A::B
and as such has no knowledge of ancestory to A.

class A
@a = "me"
class B
def t
puts @a
end
end
end

A::B.new.t

this is suggested by this returning nil.

This example, at least, makes sense to me.

class A
@a = "me" # self is the Class object A
class B
puts @a # self is the class object A::B
end
end

Instance variables are local to the object, and A and A::B aren't the same
object, so why should A::B see the instance variables of A? Similarly:

class A
@a = "me" # self is the Class object A
class B
@a = "me2" # self is the Class object A::B
def t
puts @a # self is an instance of A::B
end
end
end

All of those @a's belong to different objects, so it's not so surprising that
they aren't all the same variable. I could see how this would be a little
confusing thinking from a Java inner-class perspective, but instance
variables in Ruby are object based rather than lexically based.

- Dan
 
D

Dick Davies

* Dan Doel said:
This example, at least, makes sense to me.

class A
@a = "me" # self is the Class object A
class B
puts @a # self is the class object A::B
end
end

Instance variables are local to the object, and A and A::B aren't the same
object, so why should A::B see the instance variables of A? Similarly:

class A
@a = "me" # self is the Class object A
class B
@a = "me2" # self is the Class object A::B
def t
puts @a # self is an instance of A::B
end
end
end

All of those @a's belong to different objects, so it's not so surprising that
they aren't all the same variable. I could see how this would be a little
confusing thinking from a Java inner-class perspective, but instance
variables in Ruby are object based rather than lexically based.

Can I just throw in a link to http://www.visibleworkings.com/little-ruby/
- if I'd read Chapter 2 this morning instead of this afternoon, I wouldn't
have been so vague....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,145
Messages
2,570,824
Members
47,370
Latest member
desertedtyro29

Latest Threads

Top