问题
In Ruby, I am aware that I can do things like this:
if false
var = "Hello"
end
puts var
The application doesn't crash, and var is simply set to nil. I've read that this happens due to the way the Ruby parser works.
Why does the same not work for constants?
if false
MY_CONST = "Hello"
end
puts MY_CONST
=> uninitialized constant MY_CONST (NameError)
回答1:
TL;DR
Local variables are defined when encountered by the parser, while constants are not. However, both must be defined when evaluated by the interpreter to avoid NameError.
Analysis
Local Variables are Auto-Vivified by the Parser
Your original code doesn't actually assign a value to either the local variable or the constant. In both cases, if false is never truthy, so the assigment statements are never executed. Undefined variables and constants are handled differently by the parser, though.
Scoping issues aside, local variables are created when the parser encounters the assignment, not just when the assignment occurs. So, even though:
if false
var = "Hello"
end
never executes the assignment, it still initializes the local variable to nil.
Constants, on the other hand, are treated differently. An unknown constant (really, anything that starts with an uppercase letter) that isn't available within the current namespace will raise a NameError.
In a fresh irb session, both of these will raise NameError, but with slightly different exception messages:
puts var
#=> NameError (undefined local variable or method `var' for main:Object)
puts MY_CONST
#=> NameError (uninitialized constant MY_CONST)
However, if you change your branch logic so that an expression with an undefined variable is evaluated by the interpreter, you'll also get NameError:
if baz
puts true
end
#=> NameError (undefined local variable or method `baz' for main:Object)
Another Way to Examine the Behavior
Fire up a fresh irb session. Then:
irb(main):001:0> defined? var
#=> nil
irb(main):002:0> if false then var = 1 end
#=> nil
irb(main):003:0> defined? var
#=> "local-variable"
You can see that var is both defined and set to nil when encountered by the parser, even though the assignment expression is never evaluated. The constant is not auto-vivified, though:
irb(main):004:0> defined? MY_CONST
#=> nil
irb(main):005:0> if false then MY_CONST = 1 end
#=> nil
irb(main):006:0> defined? MY_CONST
#=> nil
irb(main):007:0> MY_CONST
#=> NameError (uninitialized constant MY_CONST)
Conclusions
While I would guess this behavior has to do with differences between the parser and the interpreter, and perhaps between the namespaces used for variable/method lookup vs. constant lookup, I can't really tell you why the difference is necessary (if it really is), or even if it's the same across all Ruby implementations. That's a question for the various Ruby engine developers, including the Ruby Core Team.
Pragmatically, though, you will always get a NameError exception when you attempt to use an undefined variable or constant. The real-world impact (if any) of this difference is therefore minimal. All languages have quirks; this may be one of them, but it's hard to see how this would cause practical problems outside of contrived examples. Your mileage may certainly vary.
回答2:
It's difficult to answer this question from the point of view of the implementation not being Ruby core member, but from the design point of view it is completely logical.
if false
var = 'hello'
end
In this case you may want to do something with your variable further, i.e.
try to reassign it with ||= or make some decisions based on whether or not
the variable is nil. And the language design will allow that because this is a variable (as in not a constant).
Constants, on the other hand, are class members and defined on the class scope:
initial_constants = self.class.constants
# Does the same as the self.class::MY_CONST = 'hello'
MY_CONST = 'hello'
> self.class::MY_CONST
=> "hello"
> self.class.constants - initial_constants
=> [:MY_CONST]
and usually you don't have evaluations on the class level. You either have a constant or you don't.
To confirm that, outside of the simple script case which is presented in your question, if you'll try to define a constant this way on the instance level:
def hello
if false
MY_CONST = 'hello'
end
end
you'll get the exception:
SyntaxError: dynamic constant assignment
And most of the real world Ruby programs are object-oriented.
Finally, one of the main questions that a language designer probably had to answer to himself are:
- Why in the world will somebody need dynamically defined constants?
- (And as a consequence) Why in the world will somebody need a stored constant with a
nilvalue?
回答3:
You never assign the constant! Compare your code to this more extended example:
if f
A=5
B=8
else
A=9
C=7
end
and remember, that everything in Ruby is executable code; there are no declarations. Now if f is truthy, you create the constants A and B, and if f is falsy, you get the constants A and C.
In your code, you have a if false, so the constant is not created.
BTW, the word constant is IMO a misnomer, because you can change constants (either with or without getting a warning, depending on how you do it).
来源:https://stackoverflow.com/questions/62302469/why-are-constants-not-initialized-like-local-variables-when-the-file-is-parsed-b