From: Yuh-Ruey Chen Date: 2008-11-02T07:13:53+09:00 Subject: Re: How to access to local variables in enclosing scopes? On Nov 1, 6:55 am, Brian Candler wrote: > There is a another reason why class and def create fresh scopes, related > to the way Ruby resolves method / local variable ambiguity. > > One of the benefits of Ruby is its lack of verbosity: in particular no > need to declare variables before using them, and no need to put '()' > after a method call if it doesn't have any arguments. The result is > compact and readable code. > > However it leaves an ambiguity: is a bareword like "x" to be interpreted > as a method call, i.e. self.x(), or as referring to a local variable? > > This is resolved statically at parse time with a simple rule. If an > assignment expression "x = ..." has been seen earlier in the current > scope (regardless of whether it is actually executed), then a bareword > "x" is a local variable, otherwise it's a method call. > > So: > > def x > "hello" > end > > def foo > if false > x = "bye" > end > puts x # prints nil (local variable, no value assigned) > end > > This is unusual, but once you've used it, it makes perfect sense. > > Now consider what would happen if 'def' didn't start a new scope. > > x = 123 > > .... 1000 lines of code > > def x > "hello" > end > > def foo > puts x # 123 instead of hello? > end > > The code would behave differently, because of the binding to the 'x' > outside. > > For me, the important thing is this: within the method body of 'foo', I > can tell for sure whether x is a local variable or a method call, by > inspecting *only* the code within that method. If it weren't for this > flushing of the scope, then I couldn't be sure without reading the > *whole* of the rest of the source file up to that point. > > The same argument applies to a class, and class methods: > > # silly example > memoize = 99 > > ... 1000 lines ... > > class Foo > extend Flurble > memoize > end > > Just by inspecting the body between 'class Foo' and 'end', I can be sure > that memoize must be a method call - perhaps something in module > Flurble, or else some method already added to class Class. Without this > scope flushing, I would have to scan the whole of the rest of the source > file. > > This behaviour may not sit comfortably with users of certain other > programming languages. I say that's fine. Ruby is a different language, > and has different idioms. If you are more comfortable with Lisp or > Haskell, then use those instead. > > There are of course pros and cons to each way of doing things, but if > you open your mind to new ways of working, you may find the pros are > surprisingly substantial. (And I'm sure the same is true when moving in > the opposite direction too :-) Brian, I am very aware of this issue. This is exactly why I didn't propose that identifiers by themselves refer to local vars in enclosing scopes. It would be incredibly fragile. To use my |scope.x| syntax example with yours: x = "hi" def x "hello" end def foo if false x = "bye" end puts x # prints nil (local variable, no value assigned) puts self.send(:x) # prints hello puts scope.x # prints hi end