Hi --
David A. Black:
The point about rb_method_missing (as the source of that error
message) is interesting, though since it's be handled by a handler
for non-existent methods, and within that handler it determines that
it also isn't a local variable, I think it's reasonable to say that
it can't tell -- or, perhaps more accurately, it *can* tell that it
is neither a (viable) method call nor a variable, but it can't tell
which you meant (hence the wording of the error message).
No, I don't see a handler which determines that it isn't a local
variable. That was already decided by the parser which created a
method-call node because no "a = 1" syntax was found. The possibility
of a local variable was long since gone.
p local_variables #=> [:a]
a = nil
Is Ruby prescient? No, it's just the parser that determines locals.
I'll stop trying to make that particular point, which is simpler than
I'm managing to make it. All I'm really saying is that, one way or
another, Ruby deems it appropriate to tell you that something that
could, syntactically, be a local variable or a method call is in fact
neither. To some extent I think that how it arrives at that conclusion
is an implementation detail (though I'm reading the stuff you're
saying about the implementation with great interest).
Take the 1.9 example again:
eval("a = 3")
p local_variables #=> []
p a #=> undefined local variable or method `a'
But
eval("a = 3")
p local_variables #=> [:a]
a = a
p a #=> 3
The parser looks for local assignment syntax and builds nodes
accordingly, then the interpreter runs.
Somewhere along the line I got the impression you were saying that it
was by parsing the argument-string, prior to executing eval, that Ruby
figured out there was an 'a' local variable. Never mind. I think I'm
back on course.
It's not really a scope issue; it's a parser issue. When we use the
same scope it fails in the same manner,
eval("a=3", binding)
p a rescue p "undef" #=> "undef"
eval("p a", binding) #=> undefined local variable or method `a'
I don't know if there is an official terminology--I would say these
two calls to 'binding' refer to the same scope while technically being
different bindings.
It's reminiscent of what happens in a block: whatever exists already,
by way of locals, is there, but anything created there does not
survive the exit. (Reminiscent, not technically connected.) It's also
a bit like the wrapper nature of things like method or float objects,
where (for example) adding a singleton method to 1.2 doesn't do any
good the next time you write 1.2.
Curiously, we can get the 1.8 behavior in 1.9 by using the same
binding,
bind = binding
eval("a=3", bind)
p a rescue p "undef" #=> "undef"
eval("p a", bind) #=> 3
That's also similar to other wrapper behavior (like saving a
particular float in a variable). It does seem like binding should be a
proxy more than a wrapper, if that makes sense.
David
--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training:
http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (
http://manning.com/black2)
http://www.wishsight.com => Independent, social wishlist management!