Class method in singleton_methods?

M

Minkoo Seo

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
 
D

dblack

Hi --

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?

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 ([email protected])
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
 
D

dblack

Hi --

Adding something to my previous reply:

Hi --

]
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 ([email protected])
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
 
S

Sam Kong

Hi --

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?

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)

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
 
D

Dominik Bathon

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

X =3D "top-level"

class C
X =3D "class-level"

class << self
def a
puts X
end
end
end

def C.b
puts X
end

class << C
[Class said:
def c
puts X
end
end

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


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

obj =3D D.new

def obj.g
puts X
end

class << obj
[D said:
def h
puts X
end
end

obj.f #=3D>class-level
obj.g #=3D>top-level
obj.h #=3D>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 =20
asked for the constant, it doesn't have C in it's ancestors, so the =20
constant lookup finds Object's X.
For obj.h the singleton class of obj (#<Class:#<D:0xb7f3bda0>>) is asked =
=20
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=3Dobj
class Object
class << $obj
def i
puts X
end
end
end

obj.i #=3D>top-level

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

1. Does # said:
no
2. Does Object (without ancestors) have a constant X =3D> yes =3D> consta=
nt =20
found

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

class D
Y =3D "D::Y"
end

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

obj.j #=3D>D::Y

Here the following happens:

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


I hope that helps,
Dominik
 
D

dblack

Hi --

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

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 ([email protected])
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
 
D

Dominik Bathon

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=
=20
show that there is a (maybe surprising) difference between:

class << obj
def h
puts X
end
end

and

$obj=3Dobj
class Object
class << $obj
def i
puts X
end
end
end
 
S

Sam Kong

Dominik said:
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
[D said:
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,

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

Sam
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top