Singleton classes and Namespce

I

Intransition

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
 
R

Robert Klemme

2010/6/6 Intransition said:
Do singleton classes not get the same namespace treatment as normal
classes?

I think so.
=A0module M
=A0 =A0class X; end
=A0end

=A0o =3D Object.new
=A0=3D> #<Object:0x7fc61f1a1e58>

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

=A0def o.x; X; end

This cannot work since X is not statically in scope.
=A0o.x
=A0=3D> NameError: uninitialized constant X
=A0 =A0 =A0 =A0 =A0from (irb):6:in `x'
=A0 =A0 =A0 =A0 =A0from (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.

=A0class O
=A0 =A0include M
=A0 =A0def x; X; end
=A0end

=A0O.new.x
=A0=3D> 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
=3D> nil
irb(main):004:0> o =3D Object.new
=3D> #<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
=3D> nil
irb(main):009:0> o.x
=3D> 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
=3D> nil
irb(main):013:0> o.y
=3D> M::X
irb(main):014:0>

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Caleb Clausen

I think so.


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.
 
R

Robert Klemme

2010/6/7 Caleb Clausen said:
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 would have expected this to work.
OTOH, constant lookup is confusing.

This way works:

=A0module M
=A0 class X; end
=A0end

=A0o =3D Object.new =A0# =3D> #<Object:0x7fc61f1a1e58>

=A0(class << o; self; end).class_eval{ include M } # =3D> #<Object:0x7fc6= 1f1a1e58>
=A0#or o.extend M as well, presumably

=A0class<<o
=A0 =A0def x; X end
=A0end

=A0o.x =A0 #=3D>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:
=A0(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 =3D Object.new
irb(main):004:1> end
=3D> #<Object:0x101792b4>
irb(main):005:0> def (M::O).x; X; end
=3D> 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 `<main>'
irb(main):007:0> module M
irb(main):008:1> def O.y; X; end
irb(main):009:1> end
=3D> nil
irb(main):010:0> M::O.y
=3D> M::X
irb(main):011:0> def (M::O).x; M::X; end
=3D> nil
irb(main):012:0> M::O.x
=3D> M::X

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Caleb Clausen

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".

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.)
 
I

Intransition

Thanks Robert and Caleb. At least it makes sense --even if it doesn't
make sense ;-)
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:

=A0module M
=A0 =A0class X; end
=A0end

=A0o =3D Object.new =A0# =3D> #<Object:0x7fc61f1a1e58>

=A0(class << o; self; end).class_eval{ include M } # =3D> #<Object:0x7fc6= 1f1a1e58>
=A0#or o.extend M as well, presumably

=A0 class<<o
=A0 =A0 def x; X end
=A0 end

=A0o.x =A0 #=3D>M::X

Seehttp://ruby.runpaint.org/variables#constantsin which the lookup
rules are summarized as:
=A0 (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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top