Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Class method in singleton_methods?

1 view
Skip to first unread message

Minkoo Seo

unread,
Mar 1, 2006, 11:39:09 PM3/1/06
to
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 Seo

dbl...@wobblini.net

unread,
Mar 2, 2006, 9:07:08 AM3/2/06
to
Hi --

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 (dbl...@wobblini.net)
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


dbl...@wobblini.net

unread,
Mar 2, 2006, 9:13:34 AM3/2/06
to
Hi --

Adding something to my previous reply:

On Thu, 2 Mar 2006, dbl...@wobblini.net wrote:

> Hi --
>
> On Thu, 2 Mar 2006, Minkoo Seo 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.

Sam Kong

unread,
Mar 4, 2006, 11:14:59 PM3/4/06
to

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 Bathon

unread,
Mar 5, 2006, 9:15:26 AM3/5/06
to
On Sun, 05 Mar 2006 05:18:39 +0100, Sam Kong <sam.s...@gmail.com> wrote:

> 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

p [self, self.ancestors] #=> [#<Class:C>, [Class, Module, Object, Kernel]]


> 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

p [self, self.ancestors] #=> [#<Class:#<D:0xb7f3bda0>>, [D, Object,
Kernel]]


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

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


dbl...@wobblini.net

unread,
Mar 5, 2006, 2:01:49 PM3/5/06
to
Hi --

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.

Dominik Bathon

unread,
Mar 5, 2006, 4:37:25 PM3/5/06
to
On Sun, 05 Mar 2006 21:15:09 +0100, <dbl...@wobblini.net> 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

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


Sam Kong

unread,
Mar 6, 2006, 3:56:47 PM3/6/06
to

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

Sam

0 new messages