From: Intransition on
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

From: Robert Klemme on
2010/6/6 Intransition <transfire(a)gmail.com>:
> Do singleton classes not get the same namespace treatment as normal
> classes?

I think so.

>  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

This cannot work since X is not statically in scope.

>  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

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

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

From: Caleb Clausen on
On 6/7/10, Robert Klemme <shortcutter(a)googlemail.com> wrote:
> 2010/6/6 Intransition <transfire(a)gmail.com>:
>> Do singleton classes not get the same namespace treatment as normal
>> classes?
>
> I think so.
>
>> 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
>
> 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.

From: Robert Klemme on
2010/6/7 Caleb Clausen <vikkous(a)gmail.com>:
> On 6/7/10, Robert Klemme <shortcutter(a)googlemail.com> wrote:
>> 2010/6/6 Intransition <transfire(a)gmail.com>:
>>> Do singleton classes not get the same namespace treatment as normal
>>> classes?
>>
>> I think so.
>>
>>>  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
>>
>> 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?

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:
>
>  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

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 `<main>'
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

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

From: Caleb Clausen on
On 6/7/10, Robert Klemme <shortcutter(a)googlemail.com> wrote:
> 2010/6/7 Caleb Clausen <vikkous(a)gmail.com>:
>> 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.)