That was unexpected -- strange scoping behavior

P

Patrick Doyle

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
 
T

Thomas B.

Patrick said:
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.
 
D

Dido Sevilla

SW50ZXJlc3RpbmcuIFNvIEkgc3VwcG9zZSB0aGF0IG1lYW5zIHRoYXQgdGhlcmUncyBhIGhvbGUg
aW4gUnVieSdzCmltcGxlbWVudGF0aW9uIG9mIGxleGljYWwgc2NvcGluZy4uLj8gSXQncyBzb3J0
IG9mIHJlbWluaXNjZW50IG9mIHRoZQpwcm9ibGVtcyBwZW9wbGUgcnVuIGludG8gd2l0aCBub24t
aHlnaWVuaWMgbWFjcm9zIGluIExpc3AgZGlhbGVjdHMuCgotLSAK5pmu6YCa44GY44KD44Gq44GE
44Gu44GM5b2T54S244Gq44KJ562U44GI44KL56eB44Gv5L2V44GM44Gn44GN44KL77yfCuaZrumA
muOBp+OCguaZrumAmuOBmOOCg+OBquOBj+OBpuaEn+OBmOOCi+OBvuOBvuaEn+OBmOOCi+OBk+OB
qOOBoOOBkeOCkuOBmeOCi+OCiO+8gQpodHRwOi8vc3Rvcm13eXJtLmJsb2dzcG90LmNvbQo=
 
T

Thomas B.

Dido said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,060
Latest member
BuyKetozenseACV

Latest Threads

Top