That was unexpected -- strange scoping behavior

Consider the following piece of code:
#!/usr/bin/env ruby

def wow(idx, donetest)
until donetest.call(idx)
puts idx
idx = idx + 1
end #until
end #def wow

idx = 2
puts “before wow, idx = #{idx}\n”
wow(3, lambda {|x| x > 5})
puts “after wow, idx = #{idx}\n”

Now, suppose I used a different dummy variable for the lambda function

puts “before wow, idx = #{idx}\n”
wow(3, lambda {|idx| idx > 5})
puts “after wow, idx = #{idx}\n”


When I just happened to use the name “idx” for my dummy variable in my
lambda function, I was quite surprised to learn that it changed the
value of “idx” in the enclosing scope. I suppose that makes sense
(given Ruby’s scoping rules), but it sure was unexpected!

–wpd

Patrick D. wrote:

Consider the following piece of code:
#!/usr/bin/env ruby

def wow(idx, donetest)
until donetest.call(idx)
puts idx
idx = idx + 1
end #until
end #def wow

idx = 2
puts “before wow, idx = #{idx}\n”
wow(3, lambda {|x| x > 5})
puts “after wow, idx = #{idx}\n”

Now, suppose I used a different dummy variable for the lambda function

puts “before wow, idx = #{idx}\n”
wow(3, lambda {|idx| idx > 5})
puts “after wow, idx = #{idx}\n”


When I just happened to use the name “idx” for my dummy variable in my
lambda function, I was quite surprised to learn that it changed the
value of “idx” in the enclosing scope. I suppose that makes sense
(given Ruby’s scoping rules), but it sure was unexpected!

–wpd

Yes, it is a small gotcha. In Ruby 1.9 this is going to work like
expected - lambda (or block) arguments will be separate from the lambda
defining scope.

There will also be an option to declare additional local variables in
lambdas. Ruby 1.8:

a=5
[3].each{|x| a=x;puts a}
a #=> 3
The a in the block refers to the outside a, and nothing can be done
about this. This might sometimes cause errors, difficult to track down.
If you don’t want the a in the block to refer to the a outside, you have
to change the variable name.

Ruby 1.9:
a=5
[3].each{|x;a| a=x;puts a}
a #=> 5

After semicolon, you can define variables that will be local in their
block or lambda, even despite name clash.

TPR.

Interesting. So I suppose that means that there’s a hole in Ruby’s
implementation of lexical scoping…? It’s sort of reminiscent of the
problems people run into with non-hygienic macros in Lisp dialects.

Dido S. wrote:

Interesting. So I suppose that means that there’s a hole in Ruby’s
implementation of lexical scoping…? It’s sort of reminiscent of the
problems people run into with non-hygienic macros in Lisp dialects.

I guess, it’s rather not a hole in the implementation, but sort of a
missed idea. The 1.8 behaviour is deterministic and documented, but
discouraged because there’s no real need to use this as a feature, and
it is not elegant because the lambda argument list “looks like” variable
definition but is not, which makes the code harder to understand. And
then the next step from ‘discouraged’ is ‘gone’, as we see in 1.9.

TPR.