On Jan 20, 2006, at 2:53 PM, Tom Allison wrote:
> This:
> Variable Scope, pages 105 through 107
> "any new locals created (in while, until, for) will be available
> afterward"
> I've never seen anything like this.
> Anything that is declared (or implicitly declared in ruby) within a  
> block
> (conditional or loop) is always out-of-scope when you leave that  
> block.

The terminology used in the Pickaxe can be inconsistent or ambiguous at
times.  My copy is littered with my own notes written in the margin as
I said "Huh?" and then later figured out what was actually meant.

Here is my go at explaining it.  It is long but I hope clear (and  
correct!).

The while, until, and for loop constructs don't create a new scope so,
from the point of view of local variables, these constructs don't change
the rules in any way.  To the extent that other languages do  
introduce a new
variable scope via their looping constructs this may be unexpected  
but the
resulting behavior, I think, is readily understood once you realize  
that no
new scope is introduced by the while, until or for constructs.  
Conditionals
don't introduce new scopes either (if/unless/case), nor does a begin/ 
end block.

Blocks *do* create a new scope and one that is somewhat different  
than people
expect.  Since Enumerable#each and Kernel#loop are methods that  
accept blocks
it *seems* as those these looping constructs have special rules but  
it is
really the blocks that introduce the scoping issue not the methods.  Any
method that accepts a block would introduce the same scoping issues,  
not just
the blocks associated with 'each' and 'loop'.

So what is special about blocks?

    a) local variables that are *created* in the block, *live* and  
*die* in the block.
       They are not visible outside the block.
    b) local variables that are visible when the block is defined are  
visible within the block
       when it executes (the block captures the lexical environment  
in which it is defined)
    c) block arguments behave like local variables, *not* like method  
arguments

Consider:

	a = 4
	[5].each { |x| puts a,x }
	puts a,x

a is visible when the block is defined (i.e. when each is called) so  
it is visible
when the block executes

x is *not* visible when the block is defined but as part of the calling
sequence, when each passes 5 to the block, x = 5 is executed.  This  
'defines'
the variable x within the block (since it didn't previously exist).   
This
is the behavior described in c) above. Since x was created in the  
block it
remains visible *only* in the block. When 'puts a,x' runs outside the  
block
x is undefined.

Now consider:

	a = 4
	x = 5
	[6].each { |x| puts a,x }
         puts a,x

The situation with a is unchanged in this example but is different  
for x.
Since x is visible when the block is defined, the assignment that is  
implicit
when the block is called, x = 6, simply changes the value of the  
existing variable x.
So, in this case, no new variable is created inside the block.   
Outside the block,
x is still visible and reflects any changes made while the block  
executed (because
the x in the block is the same as the x outside the block).

I think the thing that is most surprising about this behavior is that  
block arguments
behave like local variables and don't 'shadow' an identically named  
variable in the
enclosing scope.

Hope this helps.  And if it doesn't then feel free to point out the  
un-helpful parts.


Gary Wright