Forum: Ruby Class method in singleton_methods?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Minkoo S. (Guest)
on 2006-03-02 06:44
(Received via mailing list)
Hi group.

I've found that I have to query class methods like:

irb(main):001:0> class Foo
irb(main):002:1>   def Foo.bar
irb(main):003:2>   end
irb(main):004:1> end
=> nil
irb(main):005:0> Foo.singleton_methods
=> ["bar"]
irb(main):006:0>

It is interesting that a class method is actually a singleton method. I
know that there are tons of posting regarding metaclasses/objects.
Unfortunately, there are simply too many to read and understand all of
them. So, please forgive my naive question.

Here's the thing. I guess Foo is actually defined like:

irb(main):001:0> Foo = Class.new
=> Foo
irb(main):002:0> class << Foo
irb(main):003:1>   def Foo.bar
irb(main):004:2>   end
irb(main):005:1> end
=> nil
irb(main):006:0> Foo.singleton_methods
=> ["bar"]
irb(main):007:0>

Am I correct?

Sincerely,
Minkoo S.
unknown (Guest)
on 2006-03-02 16:07
(Received via mailing list)
Hi --

On Thu, 2 Mar 2006, Minkoo S. wrote:

> => ["bar"]
> => Foo
> irb(main):002:0> class << Foo
> irb(main):003:1>   def Foo.bar
> irb(main):004:2>   end
> irb(main):005:1> end
> => nil
> irb(main):006:0> Foo.singleton_methods
> => ["bar"]
> irb(main):007:0>
>
> Am I correct?

A class method is indeed a singleton method of a Class object.  The
term "class method" is really just a convenient label for this case,
because it occurs quite frequently.

The "def obj.meth" and "class << obj; def meth" techniques differ as
to the visibility of constants:

   X = 1
   class C
     X = 2
     def self.a
       puts X
     end
   end

   class << C
     def b
       puts X
     end
   end

   C.a               # 2  (C::X)
   C.b               # 1  (top-level X)

But in the vast majority of cases you can use them pretty much
interchangeably.


David

--
David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
unknown (Guest)
on 2006-03-02 16:13
(Received via mailing list)
Hi --

Adding something to my previous reply:

On Thu, 2 Mar 2006, removed_email_address@domain.invalid wrote:

> Hi --
>
> On Thu, 2 Mar 2006, Minkoo S. wrote:
>
[...]
>> It is interesting that a class method is actually a singleton method. I
>> know that there are tons of posting regarding metaclasses/objects.
>> Unfortunately, there are simply too many to read and understand all of
>> them. So, please forgive my naive question.
>>
[...]
>> Am I correct?
>
> A class method is indeed a singleton method of a Class object.  The
> term "class method" is really just a convenient label for this case,
> because it occurs quite frequently.

The one way in which class methods differ from other singleton methods
is that they're the only case, as far as I know, where more than one
object can call a specific singleton method:

   class C
     def self.x
     end
   end

   class D < C
   end

   D.x     # OK, because D is a subclass of C, even though x is
           # a singleton method on another object (C)

The reason is this: C's singleton class (or "metaclass", as singleton
classes of Class objects are sometimes known) is the superclass of D's
singleton class.  Since the method x resides in C's singleton class,
it is visible to instances of that singleton class (namely C), and to
instances of descendants of that singleton class (namely, D).

In other words, when you send the message "x" to D, D looks in its
singleton class, and then in the superclass of its singleton class --
and there it finds a method "x", and executes it.


David

--
David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
Sam K. (Guest)
on 2006-03-05 06:21
(Received via mailing list)
removed_email_address@domain.invalid wrote:
> > irb(main):003:2>   end
> >
> > => ["bar"]
>
>        puts X
>      end
>    end
>
>    C.a               # 2  (C::X)
>    C.b               # 1  (top-level X)
>

The visibility issue is quite confusing.
See the following example.

X = "top-level"

class C
	X = "class-level"

	class << self
		def a
			puts X
		end
	end
end

def C.b
	puts X
end

class << C
	def c
		puts X
	end
end

C.a #=>class-level
C.b #=>top-level
C.c #=>top-level


class D
	X = "class-level"
	def f
		puts X
	end
end

obj = D.new

def obj.g
	puts X
end

class << obj
	def h
		puts X
	end
end

obj.f #=>class-level
obj.g #=>top-level
obj.h #=>class-level

Very inconsistent between a class and an object.
Can somebody explain this strange behavior?

Thanks.
Sam
Dominik B. (Guest)
on 2006-03-05 16:16
(Received via mailing list)
On Sun, 05 Mar 2006 05:18:39 +0100, Sam K. 
<removed_email_address@domain.invalid>
wrote:

> 			puts X
> 		end
> 	end
> end
>
> def C.b
> 	puts X
> end
>
> class << C
	p [self, self.ancestors] #=> [#<Class:C>, [Class, Module, Object,
Kernel]]
> class D
> end
>
> class << obj
	p [self, self.ancestors] #=> [#<Class:#<D:0xb7f3bda0>>, [D, Object,
Kernel]]
> Can somebody explain this strange behavior?
Well it's not inconsistent, it's just complicated ;-)

As you can see above for C.c the singleton class of C (#<Class:C>) is
asked for the constant, it doesn't have C in it's ancestors, so the
constant lookup finds Object's X.
For obj.h the singleton class of obj (#<Class:#<D:0xb7f3bda0>>) is asked
for the constant, it does have D in it's ancestors, so D::X is found.

But actually it's even more complicated (continuing your code):

$obj=obj
class Object
	class << $obj
		def i
			puts X
		end
	end
end

obj.i #=>top-level

This is because the constant lookup first checks in all the outer
lexical
scopes if the constant is directly defined in one of the classes and
then
does a full const_get on the innermost class. So in this case, the
following happens:

1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant X
=>
no
2. Does Object (without ancestors) have a constant X => yes => constant
found

If step 2 wouldn't have found the constant then ruby would have checked
the ancestors of #<Class:#<D:0xb7f3bda0>> for the constant:

class D
	Y = "D::Y"
end

class Object
	class << $obj
		def j
			puts Y
		end
	end
end

obj.j #=>D::Y

Here the following happens:

1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant Y
=>
no
2. Does Object (without ancestors) have a constant Y => no
3. Does #<Class:#<D:0xb7f3bda0>> (including ancestors) have a constant Y
=> yes => constant found


I hope that helps,
Dominik
unknown (Guest)
on 2006-03-05 21:02
(Received via mailing list)
Hi --

On Sun, 5 Mar 2006, Dominik B. wrote:

>
> If step 2 wouldn't have found the constant then ruby would have checked the
> 		end
> yes => constant found
But Object is always an ancestor, so you don't need step 2.  Also,
step 2 (lookup in Object) doesn't really happen second; for example,
if you put yourself in D context, the lookup will hit D::X before it
hits Object::X:

$obj=obj
class Object
   class D
     class << $obj
       def k
         puts X
       end
     end
   end
end

obj.k  # class-level

So the resolution path is:

   #<Class:#<D...>>
     D                 # X found here
      Object           # X exists here but not reached

As I understand it, some of this is determined quasi-statically...
though not the singleton class parts, since those can't be determined
at all until runtime.


David

--
David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
Dominik B. (Guest)
on 2006-03-05 22:00
(Received via mailing list)
On Sun, 05 Mar 2006 20:01:49 +0100, <removed_email_address@domain.invalid> 
wrote:

>> 		def j
>> => no
>> 2. Does Object (without ancestors) have a constant Y => no
>> 3. Does #<Class:#<D:0xb7f3bda0>> (including ancestors) have a constant
>> Y => yes => constant found
>
> But Object is always an ancestor, so you don't need step 2.

Yes, but these are the steps that ruby (1.8.4) does when looking up the
constants, here is the code (eval.c):

static VALUE
ev_const_get(cref, id, self)
     NODE *cref;
     ID id;
     VALUE self;
{
     NODE *cbase = cref;
     VALUE result;

     while (cbase && cbase->nd_next) {
	VALUE klass = cbase->nd_clss;

	if (NIL_P(klass)) return rb_const_get(CLASS_OF(self), id);
	while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id,
&result)) {
	    if (result == Qundef) {
		if (!RTEST(rb_autoload_load(klass, id))) break;
		continue;
	    }
	    return result;
	}
	cbase = cbase->nd_next;
     }
     return rb_const_get(cref->nd_clss, id);
}

The while loop checks if the constant is defined directly (without
ancestors) in one of the enclosing classes, and finally if the while
loop
doesn't find the constant ruby does a full rb_const_get() on the
innermost
class (which includes the ancestors).

>          puts X
>      D                 # X found here
>       Object           # X exists here but not reached

In this case yes, but that doesn't contradict what I said.

> As I understand it, some of this is determined quasi-statically...
> though not the singleton class parts, since those can't be determined
> at all until runtime.

You can think of it as happening "quasi-statically", but actually class,
module and "class <<" are executed almost identically by the interpreter
(only different "preparations").


Dominik
unknown (Guest)
on 2006-03-05 22:16
(Received via mailing list)
Hi --

On Mon, 6 Mar 2006, Dominik B. wrote:

>>> class Object
>>>
>
> 	VALUE klass = cbase->nd_clss;
> 	cbase = cbase->nd_next;
>   }
>   return rb_const_get(cref->nd_clss, id);
> }
>
> The while loop checks if the constant is defined directly (without ancestors)
> in one of the enclosing classes, and finally if the while loop doesn't find
> the constant ruby does a full rb_const_get() on the innermost class (which
> includes the ancestors).

I think I was misunderstanding the relation between your example and
your explanation.  If I'm (now) right, you were just using Object as
an example of an enclosing class.  I had thought you were saying that
Object itself always gets checked before the ancestors.

>> As I understand it, some of this is determined quasi-statically...
>> though not the singleton class parts, since those can't be determined
>> at all until runtime.
>
> You can think of it as happening "quasi-statically", but actually class,
> module and "class <<" are executed almost identically by the interpreter
> (only different "preparations").

I mean constant resolution specifically.  But I may be behind the
times.


David

--
David A. Black (removed_email_address@domain.invalid)
Ruby Power and Light (http://www.rubypowerandlight.com)

"Ruby for Rails" chapters now available
from Manning Early Access Program! http://www.manning.com/books/black
Dominik B. (Guest)
on 2006-03-05 23:38
(Received via mailing list)
On Sun, 05 Mar 2006 21:15:09 +0100, <removed_email_address@domain.invalid> 
wrote:

> I think I was misunderstanding the relation between your example and
> your explanation.  If I'm (now) right, you were just using Object as
> an example of an enclosing class.  I had thought you were saying that
> Object itself always gets checked before the ancestors.

I used Object because the top-level X is stored in Object and I wanted
to
show that there is a (maybe surprising) difference between:

class << obj
	def h
		puts X
	end
end

and

$obj=obj
class Object
	class << $obj
		def i
			puts X
		end
	end
end
Sam K. (Guest)
on 2006-03-06 23:01
(Received via mailing list)
Dominik B. wrote:
> > 	class << self
> > class << C
> >
> > 	puts X
> > obj.f #=>class-level
> constant lookup finds Object's X.
> 		end
> 1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant X =>
>
> Here the following happens:
>
> 1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant Y =>
> no
> 2. Does Object (without ancestors) have a constant Y => no
> 3. Does #<Class:#<D:0xb7f3bda0>> (including ancestors) have a constant Y
> => yes => constant found
>
>
> I hope that helps,

Yes. That helps a lot.
Thank you very much.

Sam
This topic is locked and can not be replied to.