From: "Guy N. Hurst" Date: 2001-03-13T20:37:20+09:00 Subject: [ruby-talk:12563] Re: FEATURE REQUEST: 'my' local variables Yukihiro Matsumoto wrote: > > ... > But in reallity, > you may want some variables update, and rest of them remain isolated > from outer namespace, at the same time. So I feel <> paremeters are > better, since we can explicitly choose. But I hate the fact that the > user has to examine out scope to tell the scope of a variable. I > really want to fix this. > For some reason, I keep spending a lot of time on this issue. Considering the following... [ruby-talk:5531] >> In addition, there should be way to distinguish local variables local to the closure and local variables shared with outer scope. For most languages, declarations do the job, but I have no idea what is good for Ruby yet. << One approach is to introduce declarative 'import' and 'export' as either keywords or special method calls for non-parameter locals. IMPORT ------ foo {|x| import a if ... import :b,:c else import * end } 'import a' would mean to create a block-local variable initialized to the same value of local-variable 'a' if it exists, else set it to nil. We would still need a way to control the parameters, though. Some have mentioned _x, (x), :x, ::x, etc. I now re-introduce x~ ;-) [ruby-talk:5548] (Maybe it doesn't look so ugly anymore?) PARAMETER BACKUP ~ ------------------ In this case, the tilde is a marker that indicates the variable is block-local, but should be initialized to the associate var's value. (IOW, it is a back-up variable - just like how '~' is appended to filenames that are backups) x = 5 foo { | x~ | # also make back-up of x x += 3 puts x # 8 } # restore x from back-up puts x # 5 This solves case (1) by making a block-local duplicate of a parameter appearing beforehand. > (1) a block parameter variable names appeared BEFORE the lambda. > Since parameters in most languages are scope local, some (or > even most) expect them local to the block, even if they already > appear in the enclosing scope. [ruby-talk:5552] EXPORT ------ The 'export' feature would address the case where Ruby by default made a block-local variable due to the non- existence of that variable in the enclosing scope. foo {|x| export :q # create and/or use 'q' in enclosing scope q=5 : } This addresses case (3) without initializing the variable prior to the block. > (3) a local variable names appeared AFTER the lambda. > Since it's easy to forget about block scope rules, some (or even > most) expect the values of block local variables preserved after > the block. When he meets the `undefined local variable' error, > he should go back before the block and initialize the variable. and > ary.each do |x| > if cond?(x) > break > end > end > found = x # error; x is not defined here. becomes ary.each do |x| export :x if cond?(x) break end end found = x # no error It is also related to Dave's idea [ruby-talk:5543] >> ... then introduce some kind of operator which says "scope this variable outside the block" so that closures would work. This has the benefit of making scope-sharing explicit, ... << Except he was going with the idea of making everything block-local by default. By using the declarative 'import', no code needs to be broken. NATIVE ------ Another useful feature would address the problem in which a value is prevented from interacting with the enclosing scope. I'll call it 'native'. foo {|a,b,c| native :r,:q # optional, but ensures local to block 10.times do import :c # localize changes q = []; r = Math.sqrt(b**2-4*a*c) q << (-b + r)/2*a q << (-b - r)/2*a c += 5 end # c reverts x = q export :x # optional, but ensures it is passed on } # r,q revert The 'native' feature should also ensure the variable is initialized. This feature would solve case (2) by not having to know the variable was not previously used simply to know whether it will be block-local. > (2) a local variable names appeared IN the block. > When you are trying to use a variable local to the block, you > have to assure the name of a variable is not used outside of the > block. You have to know all local variable names. Although > it's good to know all local variable names in the method, a > programmer should be allowed to be lazy enough to avoid > searching variable names. I think these four approaches would provide the necessary optional control to allow the programmer to evade ambiguous mind benders... However, it make ruby look somewhat declarative. Yet think about private, public, attr, and the like. Plus, think about the certainty it would afford, and the time saved. Still, how could these be reduced? Well, I like the idea of a block having automatic access to everything in its surrounding scope. So we could probably eliminate 'native' and just use 'import var' with a 'var=0' type of follow-up. Furthermore, both import and export could possibly be combined into the approach as follows: foo {|q,r~| .... } # q is as usual; x and y get passed on; r,a,b revert summarizing this one, foo {|(1),(2~)| <(3)|(4)> .... } (1) *[regular params with tricky scope] (2) *[backed-up params with block-local scope] (3) *[import as block-local copies] (4) *[export to locals on scope exit] I hope these ideas made some sense... Conceptually, {|params, shadows~| .... } iterators work, closures work; optional extra control... Although implementation might not be considered right now, I do think it is important to an extent. Shadows might be done as a flagged dynavar, import in would be simply creating a block local with an initial value, and export out would simply be binding to a local (possibly creating the local) in the enclosing scope. Guy N. Hurst -- HurstLinks Web Development http://www.hurstlinks.com/ Norfolk, VA 23510 (757)623-9688 FAX 623-0433 PHP/MySQL - Ruby/Perl - HTML/Javascript