Class method in singleton_methods?


#1

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.


#2

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


#3

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 ©

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


#4

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

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


#5

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


#6

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


#7

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


#8

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


#9

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


#10

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