singleton methods vs. meta instance methods

D

Daniel DeLorme

If I understand the ruby object model correctly, then an object's
singleton methods are defined as the instance methods of that object's
meta/singleton class. So I tried to confirm this:

obj = Object.new
obj_meta = (class << obj;self;end)
def obj.foobar; end
obj.singleton_methods #=> ["foobar"]
obj_meta.instance_methods(false) #=> ["foobar"]

So far so good. But if I try the same thing with a class:

class A; end
A_meta = (class << A;self;end)
def A.foobar; end
A.singleton_methods #=> ["foobar"]
A_meta.instance_methods(false) #=> ["allocate", "superclass", "foobar",
"new"]

I understand why those 3 extra methods appear, but in that case
shouldn't they also appear as part of A.singleton_methods? Furthermore
this does not happen with builtin classes. Does anyone have an
explanation for what is going on here?

Daniel
 
R

Robert Dober

If I understand the ruby object model correctly, then an object's singlet= on
methods are defined as the instance methods of that object's meta/singlet= on
class. So I tried to confirm this:

obj =3D Object.new
obj_meta =3D (class << obj;self;end)
def obj.foobar; end
obj.singleton_methods #=3D> ["foobar"]
obj_meta.instance_methods(false) #=3D> ["foobar"]

So far so good. But if I try the same thing with a class:

class A; end
A_meta =3D (class << A;self;end)
def A.foobar; end
A.singleton_methods #=3D> ["foobar"]
A_meta.instance_methods(false) #=3D> ["allocate", "superclass", "foobar",
"new"]

I understand why those 3 extra methods appear, but in that case shouldn't
they also appear as part of A.singleton_methods? Furthermore this does no= t
happen with builtin classes. Does anyone have an explanation for what is
going on here?

Daniel
Daniel first, I had some difficulties to see what puzzles you. The
fact that meta methods are not singleton methods made perfectly sense,
but than I tried to define meta methods, which you did not, and then I
was as puzzled as you were ;)
----------------------------------------------- 8< ------------------------
507/15 > cat test1.rb && ruby test1.rb
#!/usr/bin/ruby
# vim: sw=3D2 ts=3D2 ft=3Druby expandtab tw=3D0 nu syn=3Don:
# file: test1.rb

def all_methods an_obj
p(
{ :name =3D> an_obj.name,
:meta =3D> class << an_obj; instance_methods( false ).sort end,
:single =3D> an_obj.singleton_methods( false ).sort }
)
end


def Object.answer; 42 end
def (A=3DClass::new).answer; 42 end
all_methods Object
all_methods A

class << Object; def almost; 41 end end
class << A; def almost; 41 end end
all_methods Object
all_methods A
{:meta=3D>["allocate", "answer", "new", "superclass"],
:single=3D>["answer"], :name=3D>"Object"}
{:meta=3D>["allocate", "answer", "new", "superclass"],
:single=3D>["answer"], :name=3D>"A"}
{:meta=3D>["allocate", "almost", "answer", "new", "superclass"],
:single=3D>["almost", "answer"], :name=3D>"Object"}
{:meta=3D>["allocate", "almost", "answer", "new", "superclass"],
:single=3D>["almost", "answer"], :name=3D>"A"}
------------------------------------------------ >8
---------------------------------------------------

So you are right it does not seem possible to create that kind of
diversity ourselves.
I could however not see the difference between A and Object, can you
explain please?

Cheers
Robert



--=20
Il computer non =E8 una macchina intelligente che aiuta le persone
stupide, anzi, =E8 una macchina stupida che funziona solo nelle mani
delle persone intelligenti.
Computers are not smart to help stupid people, rather they are stupid
and will work only if taken care of by smart people.

Umberto Eco
 
B

Brian Candler

Robert said:
I could however not see the difference between A and Object, can you
explain please?

The OP used an *instance* of class Object, not Object itself, and
compared it with class A, which is an instance of class Class.

Perhaps it's clearer if written thus:

obj = Object.new
obj_meta = (class << obj;self;end)
def obj.foobar; end
p obj.singleton_methods #=> ["foobar"]
p obj_meta.instance_methods(false) #=> ["foobar"]

a = Class.new
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods #=> ["foobar"]
p a_meta.instance_methods(false) #=> ["allocate", "superclass",
"foobar", "new"]
 
B

Brian Candler

Daniel said:
Furthermore this does not happen with builtin classes.

I'm not sure what you mean by that part. I get the same if I use String
instead of your class A:

a = String
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods
p a_meta.instance_methods(false)

with 1.8.6p114 I get:
["foobar"]
["allocate", "foobar", "superclass", "new"]

with 1.9.1-preview2 I get:
[:try_convert, :foobar]
[:try_convert, :foobar, :allocate, :new, :superclass]

But notice what happens if I try an *instance* of String (which is
comparable to what you were doing with an *instance* of class Object)

a = String.new
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods(false)
p a_meta.instance_methods(false)

["foobar"]
["split", "rstrip!", "to_sym", "swapcase", "chop", "foobar", "empty?",
"swapcase!", "to_f", "casecmp", "rindex", "intern", "tr", "to_s",
"reverse!", "strip!", "match", "hex", "each", "include?", "slice",
"next!", "*", "downcase!", "downcase", "sub", "+", "=~", "upto",
"concat", "lstrip", "each_byte", "succ!", "chop!", "size", "dump",
"rjust", "squeeze", "delete!", "eql?", "next", "reverse", "sub!",
"insert", "chomp", "[]", "inspect", "tr!", "replace", "[]=", "scan",
"tr_s", "lstrip!", "succ", "<<", "oct", "gsub", "capitalize!", "to_i",
"hash", "capitalize", "crypt", "index", "chomp!", "rstrip", "sum",
"upcase!", "center", "upcase", "count", "squeeze!", "unpack", "<=>",
"strip", "==", "length", "gsub!", "each_line", "slice!", "ljust",
"to_str", "%", "delete", "tr_s!"]

So you can see the instance methods of the object's metaclass include
the instance methods of that object's class. From one point of view this
seems reasonable to me; the metaclass is like a 'private subclass' to
the real class.

From another point of view, you might expect instance_methods(false) not
to return those methods. But I think that just explains why we have a
separate singleton_methods method - if you *really* want just the
singleton methods, not including the methods of the concrete class, call
that.
 
D

Daniel DeLorme

Brian said:
I'm not sure what you mean by that part. I get the same if I use String
instead of your class A:

You're right, I can't reproduce this anymore. It seems I got confused at
some point in my irb session.
But notice what happens if I try an *instance* of String (which is
comparable to what you were doing with an *instance* of class Object)

a = String.new
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods(false)
p a_meta.instance_methods(false)

["foobar"]
["split", "rstrip!", "to_sym", "swapcase", "chop", "foobar", "empty?", [snip]
"to_str", "%", "delete", "tr_s!"]

So you can see the instance methods of the object's metaclass include
the instance methods of that object's class. From one point of view this
seems reasonable to me; the metaclass is like a 'private subclass' to
the real class.
From another point of view, you might expect instance_methods(false) not
to return those methods. But I think that just explains why we have a
separate singleton_methods method - if you *really* want just the
singleton methods, not including the methods of the concrete class, call
that.

Thank you for the explanation, it does make a lot more sense now. I'm of
the second point of view; I would expect instance_methods(*true*) to
return those extra methods, but not instance_methods(*false*). But POLS
is relative, and now that I understand this behavior I can see why the
"allocate", "new" and "superclass" methods were present in A's singleton
class' instance methods; those are indeed the instance methods defined
in Class:
Class.instance_methods(false) #=> ["allocate", "superclass", "new"]

So it seems that
x.meta.instance_methods(false) == x.singleton_methods +
x.class.instance_methods(false)

I was missing that last part...

Daniel
 
R

Robert Dober

The OP used an *instance* of class Object, not Object itself, and
compared it with class A, which is an instance of class Class.
Brian I referred to exactly the same thing you do in your next post,
just do a %s/Object/String/g.
I did see his first point, although he did not give an example for the
inconsistency.
Confusing is it not ;)
Perhaps it's clearer if written thus:

obj =3D Object.new
obj_meta =3D (class << obj;self;end)
def obj.foobar; end
p obj.singleton_methods #=3D> ["foobar"]
p obj_meta.instance_methods(false) #=3D> ["foobar"]

a =3D Class.new
a_meta =3D (class << a;self;end)
def a.foobar; end
p a.singleton_methods #=3D> ["foobar"]
p a_meta.instance_methods(false) #=3D> ["allocate", "superclass",
"foobar", "new"]
No really I feel up to here we are just fine, the problem starts when
we define something in the metaclass and
see it in the singleton class, thus we can ask ourself how is it
possible that we have the difference you showed in the last two lines.


--=20
Il computer non =E8 una macchina intelligente che aiuta le persone
stupide, anzi, =E8 una macchina stupida che funziona solo nelle mani
delle persone intelligenti.
Computers are not smart to help stupid people, rather they are stupid
and will work only if taken care of by smart people.

Umberto Eco
 
R

Robert Dober

Thank you for the explanation, it does make a lot more sense now.

Does it? I was of Brian's opinion until I added a method to the meta
class and it showed up in the singleton class, that definitely broke
this explanation and now I am really interested how this happens.
To be clear I am not surprised about what you have shown so far. I am
surprised that by adding a method to the metaclass it shows up in the
singleton!!!

So we are as confused as ever but I am certain that we are confused
about more important things and on a much higher level ;).

R.

--=20
Il computer non =E8 una macchina intelligente che aiuta le persone
stupide, anzi, =E8 una macchina stupida che funziona solo nelle mani
delle persone intelligenti.
Computers are not smart to help stupid people, rather they are stupid
and will work only if taken care of by smart people.

Umberto Eco
 
B

Brian Candler

Robert said:
Brian I referred to exactly the same thing you do in your next post,
just do a %s/Object/String/g.

Maybe I overlooked something - all these classes and metaclasses can get
pretty confusing :) I was comparing the part of your code where you
wrote:

def Object.answer; 42 end

with the OP's which had:

obj = Object.new
def obj.foobar; end
No really I feel up to here we are just fine, the problem starts when
we define something in the metaclass and
see it in the singleton class

I'm not sure what the distinction is you're trying to make between
metaclass and singleton class. I believe they are two names for the same
thing.

The OP was trying to demonstrate that "an object's singleton methods are
defined as the instance methods of that object's meta/singleton class"

This was demonstrated successfully, given the additional info that
metaclass.instance_methods(false) returns both the instance methods in
the metaclass plus the instance methods of the actual class.

However, obj.singleton_methods returns only the instance methods in
obj.metaclass.

At least, that's how I understand it at the moment - I am happy to be
corrected if this is wrong.
 
R

Robert Dober

:
This was demonstrated successfully, given the additional info that
metaclass.instance_methods(false) returns both the instance methods in
the metaclass plus the instance methods of the actual class.

However, obj.singleton_methods returns only the instance methods in
obj.metaclass.
Yes it comes down to this and hopefully you are right and I am wrong,
because I am getting headaches already ;).
But... alas
you say that a meta.instance_methods returns both instance methods in
the metaclass plus the instance methods
of the actual class. I agree, you have shown this, however, :new,
:allocate & :superclass are instance methods of the metaclass, not of
the actual class, or are they not?
This is for the example A
{:meta=3D>["allocate", "answer", "new", "superclass"],
:single=3D>["answer"], :name=3D>"A"}
new is defined in class << A,

no wait a moment it is not yet I guess I see the end of the light and
the beginning of the tunnel

def (A=3DClass::new).answer; 42 end
def A.new; allocate end
all_methods A
{:meta=3D>["allocate", "almost", "answer", ***"new"***, "superclass"],
:single=3D>["almost", "answer", ***"new"***], :name=3D>"A"}

Great job Brian I got it, thanx.
Cheers
Robert

At least, that's how I understand it at the moment - I am happy to be
corrected if this is wrong.



--=20
Il computer non =E8 una macchina intelligente che aiuta le persone
stupide, anzi, =E8 una macchina stupida che funziona solo nelle mani
delle persone intelligenti.
Computers are not smart to help stupid people, rather they are stupid
and will work only if taken care of by smart people.

Umberto Eco
 
D

David A. Black

Hi --

Daniel said:
Furthermore this does not happen with builtin classes.

I'm not sure what you mean by that part. I get the same if I use String
instead of your class A:

a = String
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods
p a_meta.instance_methods(false)

with 1.8.6p114 I get:
["foobar"]
["allocate", "foobar", "superclass", "new"]

with 1.9.1-preview2 I get:
[:try_convert, :foobar]
[:try_convert, :foobar, :allocate, :new, :superclass]

But notice what happens if I try an *instance* of String (which is
comparable to what you were doing with an *instance* of class Object)

a = String.new
a_meta = (class << a;self;end)
def a.foobar; end
p a.singleton_methods(false)
p a_meta.instance_methods(false)

["foobar"]
["split", "rstrip!", "to_sym", "swapcase", "chop", "foobar", "empty?",
"swapcase!", "to_f", "casecmp", "rindex", "intern", "tr", "to_s",
"reverse!", "strip!", "match", "hex", "each", "include?", "slice",
"next!", "*", "downcase!", "downcase", "sub", "+", "=~", "upto",
"concat", "lstrip", "each_byte", "succ!", "chop!", "size", "dump",
"rjust", "squeeze", "delete!", "eql?", "next", "reverse", "sub!",
"insert", "chomp", "[]", "inspect", "tr!", "replace", "[]=", "scan",
"tr_s", "lstrip!", "succ", "<<", "oct", "gsub", "capitalize!", "to_i",
"hash", "capitalize", "crypt", "index", "chomp!", "rstrip", "sum",
"upcase!", "center", "upcase", "count", "squeeze!", "unpack", "<=>",
"strip", "==", "length", "gsub!", "each_line", "slice!", "ljust",
"to_str", "%", "delete", "tr_s!"]

So you can see the instance methods of the object's metaclass include
the instance methods of that object's class. From one point of view this
seems reasonable to me; the metaclass is like a 'private subclass' to
the real class.
From another point of view, you might expect instance_methods(false) not
to return those methods. But I think that just explains why we have a
separate singleton_methods method - if you *really* want just the
singleton methods, not including the methods of the concrete class, call
that.

I've never liked the "false" flag thing, since it's obscure to begin
with. If it doesn't actually mean "defined in exactly this class",
then I'm not sure what purpose it serves at all.

(Don't mean to sound curmudgeonly -- am multi-tasking, which almost
always means I shouldn't post to ruby-talk since whatever the main
point is is probably zooming past me :)


David
 
B

Brian Candler

Robert said:
But... alas
you say that a meta.instance_methods returns both instance methods in
the metaclass plus the instance methods
of the actual class. I agree, you have shown this, however, :new,
:allocate & :superclass are instance methods of the metaclass, not of
the actual class, or are they not?

I believe that they are instance methods of the actual class "Class".

irb(main):002:0> Class.instance_methods(false)
=> ["allocate", "superclass", "new"]
 
D

David A. Black

Hi --

Robert said:
But... alas
you say that a meta.instance_methods returns both instance methods in
the metaclass plus the instance methods
of the actual class. I agree, you have shown this, however, :new,
:allocate & :superclass are instance methods of the metaclass, not of
the actual class, or are they not?

I believe that they are instance methods of the actual class "Class".

irb(main):002:0> Class.instance_methods(false)
=> ["allocate", "superclass", "new"]

Here's a demo of the consistency from the Class case to the SomeClass
case. I'm still not convinced that it's the ideal behavior.

$ cat sing.rb
class Object
def singleton_class; class << self; self; end; end
end

class A
def x; end
end

an_a = A.new
def an_a.y; end

p A.instance_methods(false)
p an_a.singleton_class.instance_methods(false)

$ ruby sing.rb
["x"]
["y", "x"]

$ ruby -pe 'gsub(/A/,"Class")' sing.rb | ruby -
["allocate", "superclass", "new", "x"]
["y", "allocate", "superclass", "new", "x"]


David
 
B

Brian Candler

David said:
Here's a demo of the consistency from the Class case to the SomeClass
case. I'm still not convinced that it's the ideal behavior.

Why not? It seems both consistent and reasonable to me. Similarly:

$ ruby -pe 'gsub(/A/,"String")' sing.rb | ruby -
 
D

David A. Black

Hi --

Why not? It seems both consistent and reasonable to me. Similarly:

$ ruby -pe 'gsub(/A/,"String")' sing.rb | ruby -

Right; that's what I was demonstrating (the consistency, in that
instances of Class, A, String... all behave the same way). My problem
with what we're seeing is that I think the "false" flag should mean
"defined in exactly this class" -- and, in fact, that's what I thought
it did mean. Apparently it (consistently :) does not mean that, when
the receiver is a singleton class.


David
 
B

Brian Candler

David said:
My problem
with what we're seeing is that I think the "false" flag should mean
"defined in exactly this class" -- and, in fact, that's what I thought
it did mean. Apparently it (consistently :) does not mean that, when
the receiver is a singleton class.

Oh OK, that's the point Daniel was making too.

I guess instance_methods could take a tristate option:

1. instance methods of just this (singleton) class
2. instance methods of this (singleton) class plus
the actual Class it derives from
3. instance methods of the singleton class, the Class
and its ancestors

But as you say, having (true) and (false) is bad enough already :)

Currently we have obj.singleton_methods for 1 and klass.instance_methods
for (false=>2, true=>3)
 

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

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,701
Latest member
XavierQ83

Latest Threads

Top