Singleton classes and Namespce

Do singleton classes not get the same namespace treatment as normal
classes?

module M
class X; end
end

o = Object.new
=> #Object:0x7fc61f1a1e58

(class << o; self; end).class_eval{ include M }
=> #Object:0x7fc61f1a1e58

def o.x; X; end

o.x
=> NameError: uninitialized constant X
from (irb):6:in `x’
from (irb):7

The “(class << o; self; end).class_eval{ include M }” is effectively
the same as “o.extend M” but I want to emphasize the use of #include.

It of course works fine if I use an explicit class.

class O
include M
def x; X; end
end

O.new.x
=> M::X

2010/6/6 Intransition [email protected]:

Do singleton classes not get the same namespace treatment as normal
classes?

I think so.

def o.x; X; end
This cannot work since X is not statically in scope.

class O
include M
def x; X; end
end

O.new.x
=> M::X

Yes, but you also changed something else: you defined the method
inside the class body. So you did not only switch from class to
singleton class but you also changed the lookup. It works if you
define the method in the class body in the same way as you did for
class O:

irb(main):001:0> module M
irb(main):002:1> class X; end
irb(main):003:1> end
=> nil
irb(main):004:0> o = Object.new
=> #Object:0x1016f240
irb(main):005:0> (class << o; self; end).class_eval do
irb(main):006:1* include M
irb(main):007:1> def x; X; end
irb(main):008:1> end
=> nil
irb(main):009:0> o.x
=> M::X
irb(main):010:0> (class << o; self; end).class_eval do
irb(main):011:1* def y; X; end
irb(main):012:1> end
=> nil
irb(main):013:0> o.y
=> M::X
irb(main):014:0>

Kind regards

robert

On 6/7/10, Robert K. [email protected] wrote:

o = Object.new
=> #Object:0x7fc61f1a1e58

(class << o; self; end).class_eval{ include M }
=> #Object:0x7fc61f1a1e58

def o.x; X; end

This cannot work since X is not statically in scope.

Ah, but constant lookup is supposed to check the ancestors if the
constant is not found in the scope. M should be among the ancestors of
o’s singleton class, shouldn’t it? I would have expected this to work.
OTOH, constant lookup is confusing.

This way works:

module M
class X; end
end

o = Object.new # => #Object:0x7fc61f1a1e58

(class << o; self; end).class_eval{ include M } # =>
#Object:0x7fc61f1a1e58
#or o.extend M as well, presumably

class<<o
def x; X end
end

o.x #=>M::X

See http://ruby.runpaint.org/variables#constants in which the lookup
rules are summarized as:
(Module.nesting + container.ancestors + Object.ancestors).uniq
#slightly paraphrased

I guess the key point to understand is that for singleton methods,
container is the (static) module of class enclosing the singleton
method, NOT the singleton class that they affect.

On 6/7/10, Robert K. [email protected] wrote:

2010/6/7 Caleb C. [email protected]:

Ah, but constant lookup is supposed to check the ancestors if the
constant is not found in the scope. M should be among the ancestors of
o’s singleton class, shouldn’t it?

It does but you are not in the scope of the singleton class. Rather
you are in the toplevel (or any other) scope when you do “def o.x; X;
end”.

I guess the key point to understand is that for singleton methods,
container is the (static) module of class enclosing the singleton
method, NOT the singleton class that they affect.

Not sure I can follow you here. IMHO the key point is to understand
that it is a difference whether you open a scope (class, module) or
access the scope from the outside:

Sorry, I meant ‘module or class’, not ‘module of class’. Basically,
I’m agreeing with what you said just above. It just took me a little
longer to get to the same point of understanding about constant lookup
that you were at. (Like I said: constant lookup is confusing.)

2010/6/7 Caleb C. [email protected]:

Ah, but constant lookup is supposed to check the ancestors if the
constant is not found in the scope. M should be among the ancestors of
o’s singleton class, shouldn’t it?

It does but you are not in the scope of the singleton class. Rather
you are in the toplevel (or any other) scope when you do “def o.x; X;
end”.

(class << o; self; end).class_eval{ include M } # => #Object:0x7fc61f1a1e58
#or o.extend M as well, presumably

class<<o
def x; X end
end

o.x #=>M::X

That’s basically the same what I did: you create the method in the
singleton class’s scope.

See http://ruby.runpaint.org/variables#constants in which the lookup
rules are summarized as:
(Module.nesting + container.ancestors + Object.ancestors).uniq
#slightly paraphrased

I guess the key point to understand is that for singleton methods,
container is the (static) module of class enclosing the singleton
method, NOT the singleton class that they affect.

Not sure I can follow you here. IMHO the key point is to understand
that it is a difference whether you open a scope (class, module) or
access the scope from the outside:

irb(main):001:0> module M
irb(main):002:1> class X; end
irb(main):003:1> O = Object.new
irb(main):004:1> end
=> #Object:0x101792b4
irb(main):005:0> def (M::O).x; X; end
=> nil
irb(main):006:0> M::O.x
NameError: uninitialized constant X
from (irb):5:in x' from (irb):6 from /opt/bin/irb19:12:in
irb(main):007:0> module M
irb(main):008:1> def O.y; X; end
irb(main):009:1> end
=> nil
irb(main):010:0> M::O.y
=> M::X
irb(main):011:0> def (M::O).x; M::X; end
=> nil
irb(main):012:0> M::O.x
=> M::X

Kind regards

robert

Thanks Robert and Caleb. At least it makes sense --even if it doesn’t
make sense :wink:

Seehttp://ruby.runpaint.org/variables#constantsin which the lookup
rules are summarized as:
(Module.nesting + container.ancestors + Object.ancestors).uniq
#slightly paraphrased

I guess the key point to understand is that for singleton methods,
container is the (static) module of class enclosing the singleton
method, NOT the singleton class that they affect.

I supposed, but that doesn’t seem quite right, as the document pointed
out that “self.ancestors” should be looked up. Shouldn’t that catch
the singleton class too?

Honestly. Something is not right if these are not the same:

class << o
def x; X end
end

def o.x; X; end

~trans