Ralph Shnelvar 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.
-- 
Posted via http://www.ruby-forum.com/.