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|       <unchanged>       # 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              <unchanged,       # var is bound
   obj.each {              'foo=nil' can
     foo = nnn ...         be omitted>
   }

Does this leave Ruby still without variable declarations? A matter of
opinion :-)

Regards,

Brian.