Instance_variables vs. local_variables

Can anyone offer a reasonable explanation for the difference in behavior
between the methods instance_variables and local_variables?

For example

puts “before setting ‘@a’”
puts " instance variables = #{instance_variables.join(", “)}”
puts
@a = 20
puts “after setting ‘@a’”
puts " instance variables = #{instance_variables.join(", “)}”
puts

>> before setting ‘@a

>> instance variables =

>>

>> after setting ‘@a

>> instance variables = @a

>>

instance_variables only lists ‘@aafter it has been defined.

puts “before setting ‘a’”
puts " local variables = #{local_variables.join(", “)}”
puts
a = 10
puts “after setting ‘a’”
puts " local variables = #{local_variables.join(", “)}”
puts

>> before setting ‘a’

>> local variables = a

>>

>> after setting ‘a’

>> local variables = a

>>

local_variables lists ‘a’ before it is defined. It appears as though
local_variables is a list generated at parse time.

This caught me by surprise. Their similar names implied for me similar
behavior. Comments?

David Whetstone wrote:

local_variables lists ‘a’ before it is defined. It appears as though
local_variables is a list generated at parse time.

That’s exactly right, IIRC.

Ruby, unlike smalltalk, lets you add ivars at any time, so
instance_variables can only return what has been defined up to that
point.

So the implication here is that there is no way to define a local
variable at run time. Investigating this question is, in fact, what
lead me the above discovery. This is disappointing if true.

I can use method_missing to simulate late binding of local variables,
but it is inefficient to have method_missing called every time such a
variable is referenced. (No, I have not profiled to see if this is
really a performance concern, it just smells bad to me.)

Am I correct? Is what I would like to do currently impossible?

David Whetstone wrote:

So the implication here is that there is no way to define a local
variable at run time.

There’s eval with a binding. But the question is, why would you want to
define a local variable at run time? Whatever problem you’re trying to
solve, it sounds like you’re trying to solve it the wrong way.

I can use method_missing to simulate late binding of local variables,
but it is inefficient to have method_missing called every time such a
variable is referenced.

I think you’ve just explained why Ruby works this way.

A bare “foo” could be a method call, self.foo, or a local variable
reference. If this were done dynamically, every single use of “foo”
would be expensive. Even “foo = foo + 1” might be a self.foo on the RHS
the first time it was called.

But the way Ruby choses to resolve this, it knows up-front whether foo
is just a cheap offset into the stack frame.

if false
foo = nil
end
foo # foo is a local variable

Joel VanderWerf wrote:

def get_empty_binding
binding
end
b = get_empty_binding

eval(“a=1”, b)
p eval(“a”, b) # ==> 1

Not sure this is what you’re looking for.

This clears up a misperception I had when trying similar examples. For
example, while your example works, the following does not:

eval(“a=1”)
p eval(“a”) # ==> NameError: undefined local variable or method ‘a’
for main:Object

My incorrect assumption was that the object returned by Kernel::binding
(implicitly called by eval when no binding is specified) represented the
actual bindings of the current context. But it’s really only a copy,
with each call to eval retrieving a fresh copy. In practice, this:

eval(“a=1”)

is roughly equivalent to this:

do
a = 1
end

in that local symbols introduced inside are not accessible outside. So,
there really is no way to affect the bindings of an existing context,
since only a copy of the current bindings can ever be acquired. Looks
like I’m sticking with method_missing.

David Whetstone wrote:

So the implication here is that there is no way to define a local
variable at run time. Investigating this question is, in fact, what
lead me the above discovery. This is disappointing if true.

I can use method_missing to simulate late binding of local variables,
but it is inefficient to have method_missing called every time such a
variable is referenced. (No, I have not profiled to see if this is
really a performance concern, it just smells bad to me.)

Am I correct? Is what I would like to do currently impossible?

If you are eval-ing code, then you can eval against a binding, and
define locals at run time in that binding:

def get_empty_binding
binding
end
b = get_empty_binding

eval(“a=1”, b)
p eval(“a”, b) # ==> 1

Not sure this is what you’re looking for.

On Mon, Aug 10, 2009 at 3:43 PM, Joel
VanderWerf[email protected] wrote:

eval(“a=1”, b)
p eval(“a”, b) # ==> 1

Not sure this is what you’re looking for.

This means re-parsing that code (just “a” here) on every call, a
considerable expense.

One place where injecting locals into the current binding would be
useful is for compiling ERB templates into methods. The template
accepts a hash of local variables. Ideally, we could emit code like

def compiled_erb(locals = {})
locals.each { |k, v| eval “#{k} = locals[#{k.inspect}]” }
… ERB source …
end

at the top of the generated method. This would be more efficient than
evaling the locals in a binding then evaling the ERB in that binding.
The next best solution is to generate different methods for each set
of locals:

def call_erb(locals = {})
specialized_method = “compiled_erb_#{locals.keys.join}”
compile_erb(locals) unless respond_to?(specialized_method)
send specialized_method, *locals.values
end

leading to specialized methods like

def compiled_erb_abc(a = nil, b = nil, c = nil)
… ERB source …
end

def compiled_erb_foobarwhatever(foo = nil, bar = nil, whatever = nil)
… ERB source …
end

The can lead to an explosion of methods if you pass arbitrary
arguments, but in practice the set of locals keys is finite and small.
This is the approach Rails uses [1]. Merb works similarly, but uses
one method name and regenerates with different args lists.

Exploring this scenario is a fun way to get more deeply acquainted
with Ruby, but in the end I wish these hacks were unnecessary. Making
an ERB template behave like a method seems natural, but Ruby
disagrees.

Best,
jeremy

[1]
http://github.com/rails/rails/blob/master/actionpack/lib/action_view/template/renderable.rb#L55-68

Brian C. wrote:

As far as I understand, it’s not exactly a “copy”. Rather, each binding
is a linked list of frames, and if you create a new binding it’s the
same linked list but with an empty frame on top. So if you create a new
variable it goes in the top frame, but if you are searching for an
existing variable it hunts back along the list.

That makes sense.

So,
there really is no way to affect the bindings of an existing context,
since only a copy of the current bindings can ever be acquired.

No, because you can pass a binding around as an object, which means you
can manipulate it even when it isn’t the ‘current’ binding:

def define_a(b)
eval “a=1”, b
end

define_a(binding)
puts local_variables.inspect # prints [“a”]

You can also implicitly do this using the binding of a block:

def another_a(&blk)
eval “a=1”, blk.binding
yield
end

another_a do
puts local_variables.inspect # prints [“a”]
end

But these examples completely contradict the previous discussion. I was
surprised by them, because if they worked, I would have never made my
original post. But it turns out you are right, they do work … in Ruby
1.8. I should have prefaced that I am using Ruby 1.9. Apparently,
there must have been a change to how bindings work between the two
versions, because neither example works in Ruby 1.9.

David Whetstone wrote:

This clears up a misperception I had when trying similar examples. For
example, while your example works, the following does not:

eval(“a=1”)
p eval(“a”) # ==> NameError: undefined local variable or method ‘a’
for main:Object

My incorrect assumption was that the object returned by Kernel::binding
(implicitly called by eval when no binding is specified) represented the
actual bindings of the current context. But it’s really only a copy,
with each call to eval retrieving a fresh copy. In practice, this:

As far as I understand, it’s not exactly a “copy”. Rather, each binding
is a linked list of frames, and if you create a new binding it’s the
same linked list but with an empty frame on top. So if you create a new
variable it goes in the top frame, but if you are searching for an
existing variable it hunts back along the list.

eval(“a=1”)

is roughly equivalent to this:

do
a = 1
end

in that local symbols introduced inside are not accessible outside.

Yes.

So,
there really is no way to affect the bindings of an existing context,
since only a copy of the current bindings can ever be acquired.

No, because you can pass a binding around as an object, which means you
can manipulate it even when it isn’t the ‘current’ binding:

def define_a(b)
eval “a=1”, b
end

define_a(binding)
puts local_variables.inspect # prints [“a”]

You can also implicitly do this using the binding of a block:

def another_a(&blk)
eval “a=1”, blk.binding
yield
end

another_a do
puts local_variables.inspect # prints [“a”]
end

Furthermore, if you are prepared to jump through hoops, you can get
other bindings without passing them around. Google for
“binding_of_caller” or “Binding.of_caller”

David Whetstone wrote:

But it turns out you are right, they do work … in Ruby
1.8. I should have prefaced that I am using Ruby 1.9.

This may or may not be an intentional change. I googled and couldn’t
find any reference to this, so I’ve raised a ticket anyway:
http://redmine.ruby-lang.org/issues/show/1926

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs