6

Why can't I access 'B' in the following from 'A' but can from the main environment?

module A; end A.instance_eval{B=1} B #=> 1 A::B #=> uninitialized 
1

4 Answers 4

4

Because . . .

. . . constants that are not defined within a class or module are given global scope.

What matters for constant definition is the enclosing lexical scope, not the current receiver or the value of self.

Sign up to request clarification or add additional context in comments.

Comments

4

From the documentation of instance_eval (emphasis mine):

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables.

Nothing more is done here. In particular, the constant assignment runs in the enclosing context of the block. Observe:

irb(main):001:0> module A irb(main):002:1> module B; end irb(main):003:1> B.instance_eval { C = 1 } irb(main):004:1> end => 1 irb(main):006:0> A::C => 1 

Comments

3

The idiomatic way to do this would be

 A.const_set(:B, 1) A::B #=> 1 

As to why it doesn't work, in Ruby 1.8 and 1.9.2+ (it was different in 1.9.1), constant lookup is lexically scoped. I found a good blog post with an explanation. To quote:

Note that these rules apply to constant definition as well as lookup. In 1.8 and 1.9.2, a constant defined in a class_evaluated block will be defined in the enclosing lexical scope, rather than the scope of the receiver.

The same is also true for instance_eval.

Comments

1
module A; end A.class_eval{B=1} B # Undefined A::B # 1 

As for why it works, I'm not really sure. I occasionally use metaprogramming like this when creating very meta frameworks such the Small Eigen Collider, but not in day-to-day work.

6 Comments

Are you sure with this? As far as I tested on irb 1.9.3, it gives the same result as with instance_eval.
I tried it on 1.9.1 . Sorry for not mentioning that, I didn't expect it to change between tiny versions.
That's interesting. Then, how does instance_eval work in 1.9.1? Is it like how I reported?
@sawa Both B and A::B are undefined.
Wow, that's complicated. I wish someone asks this on the Ruby site.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.