From: Brian Candler Date: 2003-02-06T08:55:30+09:00 Subject: Re: Local variables & blocks On Wed, Feb 05, 2003 at 10:23:52PM +0900, dblack@candle.superlink.net wrote: > > So that x in > > > > a.each do |item| > > x = ... > > end > > p x > > > > would not be a block local variable. The code above will work without > > preceding "nil" assignment. > > I know I'm joining this thread rather late, but I have a minority > opinion anyway, so it may not matter :-) > > I'm one of the few people who have never been bothered by the way > things are now. But I realize it's going to change, so that's OK. > > However, the above example, where x comes into being in the block and > then persists, looks and feels very strange to me. I suspect that if > this is adopted, it won't be long before it starts showing up in lists > of "Ruby Gotchas" and such. Well, it's a side-effect of the new rules. The alternatives would be: (a) make _all_ assignments in blocks block-local; that would make it impossible to affect variables outside, such as found = "no" obj.each { |i| found = "yes" if some-condition } puts found (b) add declarations to the language, so you can say explicitly whether you want 'found' to be block-local or bound to the outer scope. This is against the Spirit of Ruby. Since the usage in (a) is common and convenient, it makes sense to support it. > For me, part of the problem is just the visual backwardness of it -- > having something propagate from a right-hand field back into a > left-hand field, which has a "shoe on wrong foot" feel to me. But > more fundamentally, I've lost sight of what problem people are > perceiving and trying to solve. > > Originally, the problem was shadowing. ... more importantly, lack of it. For me, the biggest problem with the current rules is the non-locality of code, or 'action at a distance'. Take the following snippet: ... obj.each { |i| j = fn(i) } ... Is 'i' a block local variable? Is 'j' a block local variable? The answer now is: "it depends". It depends on whether you happen to have used a local variable called 'i' or 'j' earlier in the method. You have to scan your whole method to find out. If you rely on one behaviour or the other - especially in threads, where you likely rely on i or j being local, then things will break badly if you (or someone else maintaining your code) adds a line or two using 'i' or 'j' as a local variable higher up. This worries me. I end up writing: Thread.new { |thread_i| thread_j = fn(thread_i) } so I can be reasonably sure that no-one will use names like 'thread_i' and 'thread_j' elsewhere in the method. > we're now focusing on having blocks work in such a way that they *do* > interact with what's around them. But at least they do so consistently. If you're writing threaded code, you will have to use a trick to get thread-local variables: def local # something like this will be yield # built into the language end Thread.new { local { |a,b,c| ... a,b,c are local } } And if you don't like that, just stick everything in a method: def mycode a = .. # all these are truly local b = .. c = .. end Thread.new { mycode } > In other words, I'm perceiving different philosophies about what and > why all of this is needed, and I'm not sure how (or whether) they can > all be accomodated. Yep, we need everything: block local variables, method local variables. We just want to get away from the heavily-overloaded syntax "foo = 0" which means several different things in different places. You can still get all the old behaviours of course: Old New --- --- obj.each { |foo| # block arg is block local ... } foo = nil foo = nil # block arg is bound (rare) obj.each { |foo| obj.each { |f| ... foo = f } ... } obj.each { obj.each { # var is block local foo = nnn ... local { |foo| } foo = nnn ... } } foo = nil } Does this leave Ruby still without variable declarations? A matter of opinion :-) Regards, Brian.