Quote:
> > > (3) some other syntax will be introduced for cases where...
> > God help us all. I thought Ruby was a simple language.
> > If a variable is only used in the scope of a block, then let the darn compiler
> > figure that out.
> That modification is not meant to spoon-feed the compiler (which does
> BTW not exist) but rather to _simplify_ the scoping rules. The AST
> walker always knows the truth about the program; the programmer might
> not. The proposed change makes it easier to realize what's going on.
That's exactly it.
I think it's a barrier to newcomers that "a=0" has various different
behaviours depending on where you put it and whether or not you had a
previous assignment to "a" elsewhere in your method. I understand it now,
but it took a while.
Furthermore, your code may sometimes *rely* on variables being block-local,
but you have no way to signal your intention other than the *absence* of an
assignment outside of the block... documentation by omission :-) I end up
using bizarre names like "thread_i" to indicate this.
Essentially, we have four different behaviours now, all of which are useful,
but all of which are implied:
(1) 'a' (assigned variable) is bound to the method's local variables
def method
a = 0
[1,2,3].each {|p|
a = p <<<
}
puts a
end
(2) 'a' is local to the block
def method
[1,2,3].each {|p|
a = p <<<
}
end
(3) 'p' (block parameter) is bound to the method's local variables
def method
p = nil
[1,2,3].each {|p| <<<
a = p
}
puts p
end
(4) 'p' is local to the block
def method
[1,2,3].each {|p| <<<
a = p
}
end
And actually cases (1) and (3) have variants where they bind to the
variables of an enclosing block, as opposed to the method itself:
(1a) 'a' is bound to an enclosing block
def method
[1,2,3].each {|p|
a = nil <<< a is local to outer block
[4,5,6].each {|q|
a = q <<< this is the same 'a' in outer block
}
puts a
}
end
(3a) 'q' is bound to an enclosing block
def method
[1,2,3].each {|p|
q = nil
[4,5,6].each {|q| <<< bound to 'q' in outer block
a = q
}
puts q
}
end
These last two really do seem to be different, because a method is not the
same as a Proc object, and so a method local variable is not the same as a
block local variable.
I think all the above cases will appear in real programs, except IMO cases
(3) and (3a) are ugly hacks which nobody should be allowed to use :-) That
is, parameters to a block should be like the formal parameters to a method,
which are always local. That is acknowledged by Matz in the proposed New
Rules.
But apart from that, we still have to signal to the interpreter whether we
want 'a' or 'p' to be local or bound to enclosing scope, and whatever you do
that's going to involve syntax rules. Let me try to summarise:
Current rules
=============
'a' bound previous assignment to 'a'
'a' local no previous assignment to 'a' before block
'|p|' bound previous assignment to 'p'
'|p|' local no previous assignment to 'p' before block
Proposed 'New Rules'
====================
'a' bound the default*
'a' local not available, use |a| in a fake block, e.g. local {|a| ...}
'|p|' bound not available, use '|q| p=q'
'|p|' local the default
*if block is nested within another block, not clear whether 'a' binds to the
entire method or to the enclosing block; this may still depend on where 'a'
was previously assigned to.
Any other set of rules is going to have to have syntax for each of these
cases, and you just choose your sugar to taste. Many have been suggested.
e.g.
Everything defaults to bound to method
======================================
'a' bound a=0
'a' local my a=0 or %a=0
'|p|' bound |p|
'|p|' local |my p| or |%p|
Everything defaults to block-local
==================================
'a' bound our a=0 or { |p| <a> ... }
'a' local a=0
'|p|' bound |our p|
'|p|' local |p|
Matz doesn't want any explicit declarations, so basically the new rules
forbid two behaviours completely ('a' local and '|p|' bound), with
workarounds if you need those behaviours.
'|p|' bound is not very useful anyway, so your fundamental problem boils
down to how to choose between the two behaviours for local variables:
max = 0
obj.each {|i|
max = i if i > max # max must be BOUND or this doesn't work
}
puts max
Thread.new {
tmp = Obj.new # tmp must be LOCAL or this doesn't work
tmp.doit
}
Regards,
Brian.
P.S. All this ignores the fact that "puts a" might be a method call or a
local variable depending on whether "a" has been assigned to previously. I
don't mind explaining that to nubies... it's having further levels of
complexity over assignments which is likely to put people off!