And while we have mentioned the new scoping rules, I think the new
rules are fine. They are simplier and more regular than the existing
rules. Its a little odd to declare block local varaibles using the
"local" trick, but then it kinda neat that the language can handle that
as well without any special rules.
All this has made look back at the Abelson and Sussman book, and I find
that even low-calorie Scheme has syntactic sugar for that usage:
(let ((<var1> <exp1>)
(<var2> <exp2>)
...
<body>)
is translated internally to
((lambda (<var1> <var2>...)
<body>)
<exp1> <exp2>...)
Similarly in Ruby, the existing usage
{
a=1 # block local variables
b=2
.. body
}
could instead be written as
proc { |a,b|
.. body
}.call(1,2)
or as
proc { |a,b|
a=1
b=2
.. body
}.call(nil,nil)
or as
def local
yield
end
local { |a,b|
a=1
b=2
.. body
}
I expect to have to get my head round this sort of thing if using Scheme
(which is why I don't!), but not in a scripting language. OTOH I suppose I
don't really care about true block local variables unless using recursion
within a block, or using threads, both of which are advanced usage anyway.
Perhaps 'local' should at least become a predefined method of Object, so we
don't all have to keep defining it ourselves
As an aside, would it be reasonable to consider a method definition as being
wrapped in a Proc object? In other words,
define myfunc(a,b,c)
.. stuff
end
could be considered the same as the following?
define myfunc(a1,b1,c1)
proc { |a,b,c|
.. stuff
}.call(a1,b1,c1)
end
If so, then there is no such thing as local variables outside of a block. It
just becomes a question of nesting: can an inner block bind to the variables
defined in an outer block? And equally can it have its own local variables
not bound to that block?
In this case, the new rules would say:
* "i=0" always binds or creates this variable in the outermost block
* "|i|" always creates a new variable bound to the current block
I think I like this. It gets rid of the "action at a distance" problem:
i=0
j=0
... 500 lines of stuff
proc { |i|
j=101
}.call(100)
The presence or absence of the earlier i=0 and j=0 assignments makes no
difference to the behaviour of i and j within the proc. Good, that's what
used to scare me.
The binding of j to the outermost block allows the assignment of 'j' to
persist after the executing of that section of code. Good.
Recursive closures continue to work, and if you really need local variables
within them, you can use the 'local' trick. That's OK.
Thread-local variables are gone. You can still achieve them using the
'local' trick, and at least it makes it explicit that this is what you want,
or you can wrap your thread code into its own method. This is the bit most
likely to break existing code, I guess.
I think I'm sold. As long as the usage |$m| and |obj.attr| is outlawed!
And given that, I don't think there should be a mandatory shadowing warning:
only when -w is specified. Whenever I write 'proc { |i| ...}' or
'local { |i| ...}' I am explicitly saying that I want a new local 'i', very
similar to how 'def foo(i)' always gives me a local 'i'.
I just spotted one inconsistency though: take the following example.
def myfunc(a,b,c)
d=...
e=...
f=...
...
myfunc(x,x,x) # recursive call
end
When such a method is called recursively, 'd', 'e' and 'f' are independent
local variables for each invocation, and there's no need to define them as
such:
def myfunc(a,b,c)
local |d,e,f| { # not needed!
...
}
end
But if I did it this way, under the new rules:
def myfunc(a1,b1,c1)
f2 = proc { |a,b,c|
d=...
e=...
f=...
...
f2(x,x,x) # the same recursion
}.call(a1,b1,c1)
end
then in this case wouldn't d,e,f be statically shared between each recursive
invocation? And so you would have to explicitly define local |d,e,f| ?
Regards,
Brian.