Comment #96

module_eval and constant definition

Added by Yuki Sonoda 277 days ago. Updated 266 days ago.

Status:Open Start:12/04/2009
Priority:Normal Due date:
Assigned to:- % Done:

0%

Category:-
Target version:-
Clause:

(eg. 11.1.2)

Page and Line:

(eg. 123:45)


Description

A constant definition under a module_eval seems to act differently from MRI 1.8.7.
class M; end
M.module_eval { C = 1 }
::C  #=> 1

In MRI 1.8, the above code defines the constant C at the top level.

But according to the spec,

11.3.1.1.1, 69:18

Otherwise, create a variable binding with name N and value V in the set of bindings of constants of the current class or module.

7.1, 23:22

[class-module-list] : A logical stack of lists of classes or modules. The class or module at the head of the list which is on the top of the stack is called the current class or module .

15.2.2.3.15, 132:21

  • Create a new list which has the same members as those of the list at the top of [class-module-list] , and add M to the head of the newly created list. Push the list onto [class-module-list].

Thus, the spec seems to say the constant C should be defined under the module M.

I think this is because the evaluation context in the spec has only class-module-list. On the other hand MRI 1.8 has ruby_class stack and ruby_cref stack.

History

Updated by Shugo Maeda 276 days ago

Yuki Sonoda wrote:

I think this is because the evaluation context in the spec has only class-module-list. On the other hand MRI 1.8 has ruby_class stack and ruby_cref stack.

To describe the behavior of Module#class_eval in 1.8, we descirbed in Step b of 15.2.2.3.15 Module#class_eval (116:26) as follows:

In Step d and e of §11.2.2, a conforming processor may ignore M which is added to the head of the top of [class-module-list] as described above, except when referring to the current class or module in a method-definition (see §13.3.1), an alias-statement (see §13.3.6), or an undef-statement (see §13.3.7).

What do you think of it?

I changed the behavior of Module#class_eval in 1.9 the same as 1.8 (regardless of the standardization),
so it may be better to change "may ignore" to "shall ignore."
FYI, 1.9 does not have ruby_class, so I implemented the behavior of 1.8 as described above.

Updated by Yuki Sonoda 269 days ago

To describe the behavior of Module#class_eval in 1.8, we descirbed in Step b of 15.2.2.3.15 Module#class_eval (116:26) as follows:

In Step d and e of §11.2.2, a conforming processor may ignore M which is added to the head of the top of [class-module-list] as described above, except when referring to the current class or module in a method-definition (see §13.3.1), an alias-statement (see §13.3.6), or an undef-statement (see §13.3.7).

What do you think of it?

I see. It seems sufficient.

But the sufficiency is not trivial. There may be a mistake that makes Ruby 1.8's behavior not conforming.
Is it safer to distinguish ruby_cref and ruby_class as a virtual processing model in the spec?

Updated by Shugo Maeda 267 days ago

Yuki Sonoda wrote:

But the sufficiency is not trivial. There may be a mistake that makes Ruby 1.8's behavior not conforming. Is it safer to distinguish ruby_cref and ruby_class as a virtual processing model in the spec?

I admit that the draft may mislead readers.

How about to add the following description to 7.1 Contextual attributes instead of adding an attribute of an execution context?

[class-module-list] A logical stack of lists of classes or modules. The class or module at the head of the list which is on the top of the stack is called the current class or module. A conforming processor shall ignore elements which are added by the following methods, except when referring to the current class or module in a method-definition (see §13.3.1), an alias-statement (see §13.3.6), or an undef-statement (see §13.3.7):

  • the method class eval of the class Module (see §15.2.2.3.15)
  • the method module eval of the class Module (see §15.2.2.3.35)
  • the method instance eval of the class Kernel (see §15.3.1.2.18)

Updated by Bjorn De Meyer 266 days ago

With the example you give, in Ruby 1.9.1, the constant C is defined as M::C. To me, that is more logical than what happens in 1.8.x. So I think that the the standard should allow both 1.8.x and 1.9.1 behavior somehow. But I don't have much ideas on how to specify this exactly, sorry.

Updated by Shugo Maeda 266 days ago

Bjorn De Meyer wrote:

With the example you give, in Ruby 1.9.1, the constant C is defined as M::C. To me, that is more logical than what happens in 1.8.x. So I think that the the standard should allow both 1.8.x and 1.9.1 behavior somehow. But I don't have much ideas on how to specify this exactly, sorry.

The behavior of Ruby 1.9.1 was reverted lately.

I had also believed that the behavior of Ruby 1.9.1 was more consistent than that of 1.8.
However, I realized that the behavior of Ruby 1.9.1 is not so useful hearing Yehuda Katz's opinion in the thread that starts with [ruby-core:26774].

Updated by Bjorn De Meyer 266 days ago

Hmm, now I also read that thread, and I can see the very valid point of having lexical scoping work well always. Lua, for example, also has pure lexical scoping. One reason I like the idea of lexical scoping is that it will make writing a Ruby to machine-code compiler a lot easier. So, I withdraw my previous comment.

In stead, if it makes sense to both you, we could mention that constants are lexically scoped in Ruby?

Updated by Shugo Maeda 266 days ago

Bjorn De Meyer wrote:

Hmm, now I also read that thread, and I can see the very valid point of having lexical scoping work well always. Lua, for example, also has pure lexical scoping. One reason I like the idea of lexical scoping is that it will make writing a Ruby to machine-code compiler a lot easier. So, I withdraw my previous comment.

In stead, if it makes sense to both you, we could mention that constants are lexically scoped in Ruby?

Technically, constants are not lexically scoped because they are lookuped according to an execution context at runtime.

Updated by Bjorn De Meyer 266 days ago

Shugo Maeda wrote:

Technically, constants are not lexically scoped because they are lookuped according to an execution context at runtime.

That's true as well, although, at least in this case, they behave "as if" they were lexically scoped, which is nice for the reasons I stated earlier. But for now, I have no further suggestions for this comment, so I'll leave it at that. Thanks for listening and for the explanation!

Also available in: Atom PDF