From: dflanagan@... Date: 2007-01-20T05:25:06+09:00 Subject: Re: local variable assertion On Jan 19, 5:58 am, "gga" wrote: > > Now I haven't written enough Ruby code to know whether this is really a > > problem in practice or if it is just a theoretical concern.I think it is more theoretical, as that would indicate you are really > writing VERY long functions. > Remember that in Ruby, global variables are $, instance variables are @ > and class variables are @@, so there's a very rare chance of conflict. I disagree. Local variables are used most often. I see a good chance of getting bitten by this problem, especially when using generic variable names like i or x as block parameters to loop iterators. Suppose you've got a simple loop to iterate through an array data.each { |x| x*x} Now you refactor some code and end up cutting-and-pasting that loop into a method that happens to use x as a parameter. Suddenly your loop behaves differently. x is no longer local to the block and it overwrites the local variable in your method. > That being said, your code can be done simpler, like: > > def let(*syms, &block) > raise "No block provided for undefined?" unless block_given? > > syms.each do |sym| > value = eval("defined? #{sym.to_s}", block) > next if !value > raise NameError.new("#{sym} is already a #{value}") > end > > yield > end This actually breaks the second-use case for my method. If the block expects parameters, then I want my method to return the block so that it can be passed on to the calling method. This allows me to use local() without having to nest blocks. Consider this invocation: data.each &local(:x) {|x| puts x*x } The block is passed to local, which checks that it is safe to use x as a variable in the block. Then local() returns the block, which gets passed, in turn, to the each() iterator. The & and the required parentheses make this syntax a little messy but it allows one block instead of two. > # block check > let( :x ) { x = 20 } > > begin > let( :x ) { p 'never run' } > rescue > end I believe this would actually print 'never run'. Since x is not a local variable, its use in the first block remains local to that block. > # Forgot block... > let( :x ) This won't work: the block is needed to pass to eval() for checking for the existance of the local varables. Otherwise, I'm just checking for local varaibles inside the local() method itself. David