Ralph S. wrote:
BC> (*) Unless you pass a Binding object explicitly, in which case it is
BC> possible to eval code which affects local variables in other scopes.Could I ask you to explain this REALLY SLOWLY so that people like me can
understand this … PLEASE.
A bit of code will speak louder than words.
def increment_a(b)
eval “a += 1”, b
end
a = 123
increment_a(binding)
puts a # prints 124
What’s going on?
Well, you probably know that a method (def … end) starts a new local
variable scope, so normally inside method increment_a you would not have
access to any local variables defined outside.
The method Kernel#binding returns an instance of a Binding object, which
gives you access to the current local variable scope. We pass that
Binding object as a parameter to increment_a, which can then eval some
code in that scope.
What Binding gives you is access to the “activation record” - the place
where local variables are stored. In C this would be called the “stack
frame”, but in Ruby it’s more like a heap than a stack, since bits of it
can persist after the method returns.
Creating an explicit Binding object is one way to make the activation
record persist and/or pass it around for remote access.
You also get an implicit Binding whenever you pass a block:
def inc_a(&blk)
yield
eval “a += 1”, blk.binding
end
a = 123
inc_a { puts “Hello” }
puts a
Another way to persist an activation record is to create a “closure”.
Normally, when a method returns, its activation record is
garbage-collected, because all its local variables drop out of scope.
However, if you create an anonymous function which uses that activation
record, it will persist as long as that function persists.
def make_a_counter
a = 0
lambda { a += 1 }
end
c1 = make_a_counter
c2 = make_a_counter
puts c1.call # 1
puts c1.call # 2
puts c2.call # 1
puts c2.call # 2
Each time you call make_a_counter it creates its own local variable
scope, in which ‘a’ is defined. Then it returns a function which
increments that particular instance of ‘a’.
So c1 and c2 are lambdas which increment different instances of ‘a’.
They both carry state. You can even bootstrap objects this way. Closures
are a standard concept in many high-level languages, and you can google
for more info.
The point is: in Ruby, none of these mechanisms lets me obtain a pointer
or reference to the particular slot in the activation record which holds
the current value of variable ‘a’. Therefore there is no “pass by
reference” of the kind you originally referred to.
But I can get a handle to the entire activation record, and then eval
code which manipulates ‘a’ directly.
Regards,
Brian.