4 ms·
Scoping. I got bitten by a huge bug in ruby's scoping last night: irb> [1,2,3].collect{|n| n+1} => [2, 3, 4] irb> n NameError: undefined local
by akkartik 19y ago
Scoping. I got bitten by a huge bug in ruby's scoping last night:
irb> [1,2,3].collect{|n| n+1}
=> [2, 3, 4]
irb> n
NameError: undefined local variable or method `n' for main:Object # Expected behavior - n's scope is restricted.
irb> n = 0
=> 0
irb> [1,2,3].collect{|n| n+1}
=> [2, 3, 4]
irb> n
=> 3 # BAD
I have ruby 1.8.4. Most recent version is 1.8.6. Gotta go see if it's a known bug or worse if it's supposed to work this way..
Edit: elided profanity.
- ambition 19y agoIt is supposed to work this way for some good reasons. But! Enough people have had the same problem as you that new versions of Ruby behave the way you expect.
- raganwald 19y agoHelp me understand: what are the good reasons for the parameters of a block overwriting variables in an enclosing scope? That is, we are talking about: i = 100 x = 0 [1,2,3].each { |i| x += i } I understand why it is incredibly helpful for x to refer to a variable in the enclosing scope (even if we could accomplish this same effect with a fold). What are the use cases for the parameter i referring to a variable in the enclosing scope?
- ambition 19y agoOh, you're right, I had missed that. Yeah, that's dumb. I can't think of any iterators that would leave useful information in the parameters, either.
- brlewis 19y agoAs someone who's used to Scheme (lexical scoping), I have to say that's pretty bizarre. That's my second disappointment with Ruby blocks. The first was learning you could only have one block argument. Do they resemble first-class objects at all? Can you assign a variable to a block? If you were to assign { |i| x += i } to a variable outside that enclosing scope, and then used that variable in that scope, would i be overwritten in that call to the block?
- raganwald 19y ago"you could only have one block argument" Do you mean one method can only have one block?
- brlewis 19y agoHaving only read about Ruby and not coded in it, I'm not sure what the correct terminology is. I'm talking about doing something like this: someObject.someMethod({block1},{block2})
- tptacek 19y agoYou can't pass more than one block literal to a method. But you can certainly pass multiple blocks; they're objects, just like anything else. Just add the token "lambda" to your expression: def foo(x, y); x.call(y.call); end foo(lambda {|x| 2+x}, lambda {1}) # => 3 It seems to me that in this case, the language degrades to approximately the same level as Common Lisp.
- brlewis 19y agoIt's good to see that an upgrade to approximately the power of CL is possible in this case. Still, I feel a little let down. When I first saw Ruby block literals, I said to myself, "Hey, that's a great, concise syntax for lambda. Languages really are getting more lisp-like all the time." But now I know that Ruby block literals aren't really lambda. It does look like Ruby lambdas really are lambdas, though.
- brlewis 19y agoCould you give a more complete example? I'm getting sane behavior in Ruby 1.8.6: irb(main):010:0> f = lambda { i = 100; x = 0; [1,2,3].each { |i| x += i }; x} => #<Proc:0x05011b34@(irb):10> irb(main):011:0> f.call() => 6
- raganwald 19y agoyou missed the fun: macbook-pro-2:trunk raganwald$ ruby -v => ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.10.1] macbook-pro-2:trunk raganwald$ irb irb(main):001:0> lambda { i = 100; x = 0; [1,2,3].each { |i| x += i }; i }.call => 3
- tptacek 19y agoYour "bug" is lexical scoping?
- akkartik 19y agoNo, it's lack of consistent lexical scoping. Either ruby blocks create a new scope or they do not. Which is it?
- tptacek 19y agoThey create new scopes, and close over the their enclosing environment. In other words, it supports both block-local variables and closures.
- akkartik 19y agoI don't understand. You mean there's a good reason for it to overwrite variables in outer scopes if they exist? Is there any way to specify if I want block-local or not? The way python deals with scopes is to provide read access to outer scopes if there are no writes to a variable.
- tptacek 19y agoOf course there is: if you couldn't access variables in the enclosing scopes, blocks would be drastically less useful. Ruby uses anonymous functions where Python would use a loop construct.
- akkartik 19y ago[We took this conversation offline. it seems this is a known gotcha (e.g. http://www.osix.net/modules/article/?id=401 http://www.osix.net/modules/article/?id=401), and may be changed in ruby 2.0 (http://groups.google.com/group/sbonrails/browse_thread/thread/cfcfdad71d8f786c http://groups.google.com/group/sbonrails/browse_thread/threa...). Thanks tptacek.]
- raganwald 19y agoYes, it is a known problem, and I have also found myself editing my own profanity: http://weblog.raganwald.com/2007/02/why-ruby-is-not-acceptable.html http://weblog.raganwald.com/2007/02/why-ruby-is-not-acceptab...