Attr_accessor vs. local variable

Ruby not seem to predictably recognize attribute accessors (including
setters) as such unless preceded by “self.attr”. But it recognizes other
self messages fine. I suspect this has something to do with its rule on what
is deemed a local variable.

What rule / ambiguity causes this?

class A
attr_accessor :x, :y

def initialize
p x # local or @x?
x=5 # does not call self.x=
self.y=5 # sets @y
foo # calls self.foo
end

def foo
p 'foo’
end

end

p A.new.x
p A.new.y

This makes sense. When you ‘p x’, all ruby knows about at that point is
the accessor method.

So p x == p @x

When you get to x=5, though, Ruby says, “Ah! A local variable” and
assigns 5 to the local variable x.

See below:

enigodev src % irb
irb(main):001:0> def x
irb(main):002:1> 7
irb(main):003:1> end
=> nil
irb(main):004:0> puts x
7
=> nil
irb(main):005:0> x = 4
=> 4
irb(main):006:0> puts x
4

Kirk Haines

···

On Sat, 24 Apr 2004, Its Me wrote:

Ruby not seem to predictably recognize attribute accessors (including
setters) as such unless preceded by “self.attr”. But it recognizes other
self messages fine. I suspect this has something to do with its rule on what
is deemed a local variable.

What rule / ambiguity causes this?

class A
attr_accessor :x, :y

def initialize
p x # local or @x?
x=5 # does not call self.x=
self.y=5 # sets @y
foo # calls self.foo
end

def foo
p ‘foo’
end

end

p A.new.x
p A.new.y

Hi –

Ruby not seem to predictably recognize attribute accessors (including
setters) as such unless preceded by “self.attr”. But it recognizes other
self messages fine. I suspect this has something to do with its rule on what
is deemed a local variable.

That’s correct, except that there’s nothing unpredictable about it, and
it’s not specific to methods defined with the attr_* family.

What rule / ambiguity causes this?

class A
attr_accessor :x, :y

def initialize
p x # local or @x?

Strictly speaking, neither; in your example, it’s a call to the method
x (which you happen to have defined as <def x; @x; end>, but could
have defined in many different ways).

x=5      # does not call self.x=

If it looks like a local variable assignment, then Ruby will define a
local variable of that name at compile time, and will execute the
assignment at runtime. Since all bare “var = value” expressions get
claimed by this process, the way to insulate calls to ‘=’ methods is
to make the receiver explicit:

self.y=5      # sets @y

which means it can no longer be interpreted as a variable assignment.

David

···

On Sat, 24 Apr 2004, Its Me wrote:


David A. Black
dblack@wobblini.net

Kirk Haines said:

This makes sense. When you ‘p x’, all ruby knows about at that point is
the accessor method.

Actually, by the time you execute ‘p x’, Ruby has already compiled the
method and knows that x is a local variable (since it saw an assignment to
x).

– Jim Weirich

True. It figures out the local variables at compile time (thus all the
fun with eval and locals), but behaviorally, it seems like since the flow
of execution has not reached the local variable declaration has not been
reached yet, Ruby call the method x() instead. Once the first assignment
takes place, though, the local variable takes precedence over the method.

It makes sense. Am I misinterpretting what’s happening?

Kirk Haines

···

On Sat, 24 Apr 2004, Jim Weirich wrote:

Kirk Haines said:

This makes sense. When you ‘p x’, all ruby knows about at that point is
the accessor method.

Actually, by the time you execute ‘p x’, Ruby has already compiled the
method and knows that x is a local variable (since it saw an assignment to
x).

Kirk Haines said:

Kirk Haines said:

This makes sense. When you ‘p x’, all ruby knows about at that point
is
the accessor method.

Actually, by the time you execute ‘p x’, Ruby has already compiled the
method and knows that x is a local variable (since it saw an assignment
to
x).

True. It figures out the local variables at compile time (thus all the
fun with eval and locals), but behaviorally, it seems like since the flow
of execution has not reached the local variable declaration has not been
reached yet, Ruby call the method x() instead. Once the first assignment
takes place, though, the local variable takes precedence over the method.

It makes sense. Am I misinterpretting what’s happening?

I stand corrected. References to ‘x’ that are lexically before the
assignment are treated as method calls. References after the the
assignment are treated as local variables. But notice it has nothing to
do with executeing the assignment.

That leads to the following example …

$ cat x.rb
def x
“METHOD CALL”
end
def f
i = 2
while i > 0
puts x
x = “LOCAL VARIABLE”
puts x
i -= 1
end
end
f
$
$ ruby x.rb
METHOD CALL
LOCAL VARIABLE
METHOD CALL
LOCAL VARIABLE

···

On Sat, 24 Apr 2004, Jim Weirich wrote:


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Great example! I would not have expected it to work that way, though I
understand now that I see it. Thanks much.

Kirk Haines

···

On Sat, 24 Apr 2004, Jim Weirich wrote:

I stand corrected. References to ‘x’ that are lexically before the
assignment are treated as method calls. References after the the
assignment are treated as local variables. But notice it has nothing to
do with executeing the assignment.

That leads to the following example …

$ cat x.rb
def x
“METHOD CALL”
end
def f
i = 2
while i > 0
puts x
x = “LOCAL VARIABLE”
puts x
i -= 1
end
end
f
$
$ ruby x.rb
METHOD CALL
LOCAL VARIABLE
METHOD CALL
LOCAL VARIABLE