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