Inheritance and meta-classes

A

Adam Salter

Hi all,

OK. I'm trying to work out where a method is defined in Rails.
It's in a test, and the specific function that's being called is
'post', but my question is not a Rails question, it's Ruby.

How do I find where a function is defined in the inheritance tree?
Because of metaclass inheritance being cyclical I don't know where to
stop looking.

I made the following as a test but I still can't find the function
being called so I'm guessing I'm missing some of the inheritance tree...

module Debugs
def find_respond_to_in_class_heirarchy(obj, method)
if obj.respond_to?:)superclass)
arr = find_respond_to_in_class_heirarchy(obj.superclass, method)
arr.unshift([obj.class.respond_to?(method),
obj.metaclass.respond_to?(method), obj.superclass.respond_to?(method)])
elsif obj.nil?
arr = [[nil.class.respond_to?(method), nil.metaclass.respond_to?
(method), NoMethodError]]
else
arr = find_respond_to_in_class_heirarchy(obj.class, method)
arr.unshift([obj.class.respond_to?(method),
obj.metaclass.respond_to?(method), NoMethodError])
end
arr
end
end

class Object
include Debugs
def metaclass
class << self
self
end
end
end

Wow this whole metaclass, class, object, module relationship is pretty
mindbending...

Cheers,
-Adam
 
A

Adam Salter

Any suggestions?
(or has the subject been done to death...)

I still don't get how you can query and see where in the chain a
method got added to an object...

I've now found that you can do:
obj.class.ancestors # => Array

But that still doesn't include metaclasses...

How does Ruby know??
 
R

Robert Klemme

How do I find where a function is defined in the inheritance tree?
Where exactly do you see cycles? I am not aware of any.

2007/12/13 said:
I still don't get how you can query and see where in the chain a
method got added to an object...

I've now found that you can do:
obj.class.ancestors # => Array

But that still doesn't include metaclasses...
Correct.

How does Ruby know??

The meta class is there but it's hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

irb(main):001:0> c1 = Class.new
=> #<Class:0x7ff9f70c>
irb(main):002:0> def c1.x; "X" end
=> nil
irb(main):003:0> c1.x
=> "X"
irb(main):004:0> c2 = Class.new c1
=> #<Class:0x7ff8f4b0>
irb(main):005:0> c2.x
=> "X"
irb(main):006:0> c2.ancestors
=> [#<Class:0x7ff8f4b0>, #<Class:0x7ff9f70c>, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> ["extend"]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> ["x", "extend"]

Note also that #respond_to? is a weak method to determine whether an
instance will respond to a method, because of #method_missing which I
believe is used a lot by Rails. Also, it is not called on the class
but on the object.

You can use #instance_method(s) to check whether a class implements a
certain instance method but as I said, you might not be lucky.

Kind regards

robert
 
R

Rick DeNatale

Where exactly do you see cycles? I am not aware of any.

shadowfax:~/ssanta rick$ irb
irb(main):001:0> def meta
irb(main):002:1> class << self
irb(main):003:2> self
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> meta
=> #<Class:#<Object:0x349a4>>
irb(main):007:0> Array.meta
=> #<Class:Array>
irb(main):008:0> Array.meta.superclass
=> #<Class:Class>
irb(main):009:0> Array.meta.superclass.superclass
=> # said:
2007/12/13 said:
I still don't get how you can query and see where in the chain a
method got added to an object...

I've now found that you can do:
obj.class.ancestors # => Array

But that still doesn't include metaclasses...
Correct.

How does Ruby know??

The meta class is there but it's hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

irb(main):001:0> c1 = Class.new
=> #<Class:0x7ff9f70c>
irb(main):002:0> def c1.x; "X" end
=> nil
irb(main):003:0> c1.x
=> "X"
irb(main):004:0> c2 = Class.new c1
=> #<Class:0x7ff8f4b0>
irb(main):005:0> c2.x
=> "X"
irb(main):006:0> c2.ancestors
=> [#<Class:0x7ff8f4b0>, #<Class:0x7ff9f70c>, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> ["extend"]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> ["x", "extend"]

I don't see any metaclasses here. c1 and c2 are simply anonymous classes.

A metaclass doesn't sit between an instance and its class. A singleton
class of an instance would, but that's not a metaclass (although the
way the two terms get conflated I can see where that's a common
misconception). A metaclass is a singleton 'class' which holds the
behavior of a class.

Here's a simplified diagram
http://myskitch.com/rubyredrick/skitched-20071213-093049/

It's simplified since it doesn't include the inclusion of Kernel by Object.
 
R

Robert Klemme

shadowfax:~/ssanta rick$ irb
irb(main):001:0> def meta
irb(main):002:1> class << self
irb(main):003:2> self
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> meta
=> #<Class:#<Object:0x349a4>>
irb(main):007:0> Array.meta
=> #<Class:Array>
irb(main):008:0> Array.meta.superclass
=> #<Class:Class>
irb(main):009:0> Array.meta.superclass.superclass
=> #<Class:Class>

Ah, now I see! Should've put on my glasses. ;-)
2007/12/13 said:
I still don't get how you can query and see where in the chain a
method got added to an object...

I've now found that you can do:
obj.class.ancestors # => Array

But that still doesn't include metaclasses... Correct.

How does Ruby know??
The meta class is there but it's hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

irb(main):001:0> c1 = Class.new
=> #<Class:0x7ff9f70c>
irb(main):002:0> def c1.x; "X" end
=> nil
irb(main):003:0> c1.x
=> "X"
irb(main):004:0> c2 = Class.new c1
=> #<Class:0x7ff8f4b0>
irb(main):005:0> c2.x
=> "X"
irb(main):006:0> c2.ancestors
=> [#<Class:0x7ff8f4b0>, #<Class:0x7ff9f70c>, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> ["extend"]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> ["x", "extend"]

I don't see any metaclasses here. c1 and c2 are simply anonymous classes.

According to the definition given by you the class that's used in
statement 8 is a meta class.
A metaclass doesn't sit between an instance and its class. A singleton
class of an instance would, but that's not a metaclass (although the

Since a metaclass - according to your definition - is a class's
singleton class it sits between the instance (the class object, e.g.
Array) and its class (Class) in the same way an ordinary's object's
singleton class does - which is also shown by the image you referred to
in your posting.
way the two terms get conflated I can see where that's a common
misconception). A metaclass is a singleton 'class' which holds the
behavior of a class.

You mean "class<<Array;self;end", do you?
Here's a simplified diagram
http://myskitch.com/rubyredrick/skitched-20071213-093049/

It's simplified since it doesn't include the inclusion of Kernel by Object.

Nice colors. ;-) I reckon Array' is intended to mean the singleton
class of object Array. I am not sure I get your point where you wanted
to correct me. As far as I can see we are completely in sync.

Generally singleton classes of class instances and of ordinary instances
behave the same. The only difference I can see so far is that, since
classes can inherit from each other, a subclass also shares instance
methods defined for its superclass (see my irb session). Btw, something
similar happens with instances when cloned:

irb(main):001:0> o = Object.new
=> #<Object:0x7ff9e474>
irb(main):002:0> def o.xxx;1;end
=> nil
irb(main):003:0> o.xxx
=> 1
irb(main):004:0> class <<o;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):005:0> o1 = o.clone
=> #<Object:0x7ff80eec>
irb(main):006:0> o1.xxx
=> 1
irb(main):007:0> class <<o1;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):008:0> o2 = o.dup
=> #<Object:0x7ff73cec>
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx' for #<Object:0x7ff73cec>
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Kind regards

robert
 
R

Rick DeNatale

On 13.12.2007 15:33, Rick DeNatale wrote:

Nice colors. ;-) I reckon Array' is intended to mean the singleton
class of object Array. I am not sure I get your point where you wanted
to correct me. As far as I can see we are completely in sync.

Yes I copied the classname' notation from the diagrams Dave Thomas
uses in the pickaxe.

The statement I wanted to correct, maybe I misunderstood it was:
The meta class is there but it's hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

I read object as an instance of "its class." The above statement is
wrong in two regards.

First, the metaclass isn't on the inheritance chain of the instance,
it's on the inheritance chain of the class, but that's a horse of a
different color.

Second, in the klass chain it's on the other side of the class, it goes:

instance - klass-> class -klass-> class'

NOT

instance -klass-> class' -klass-> class
Generally singleton classes of class instances and of ordinary instances
behave the same. The only difference I can see so far is that, since
classes can inherit from each other, a subclass also shares instance
methods defined for its superclass (see my irb session). Btw, something
similar happens with instances when cloned:

irb(main):001:0> o = Object.new
=> #<Object:0x7ff9e474>
irb(main):002:0> def o.xxx;1;end
=> nil
irb(main):003:0> o.xxx
=> 1
irb(main):004:0> class <<o;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):005:0> o1 = o.clone
=> #<Object:0x7ff80eec>
irb(main):006:0> o1.xxx
=> 1
irb(main):007:0> class <<o1;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):008:0> o2 = o.dup
=> #<Object:0x7ff73cec>
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx' for #<Object:0x7ff73cec>
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Not quite the same, what happens here is that cloning an object with a
singleton class gives the clone a singleton class which looks like the
original object's singleton class:

k$ irb
irb(main):001:0> def meta
irb(main):002:1> class << self;self;end
irb(main):003:1> end
=> nil
irb(main):004:0> o = Object.new
=> #<Object:0x89724>
irb(main):005:0> o.meta
=> #<Class:#<Object:0x89724>>
irb(main):006:0> o1 = o.clone
=> #<Object:0x82c44>
irb(main):007:0> o1.meta
=> #<Class:#<Object:0x82c44>>
irb(main):008:0> o.meta == o1.meta
=> false

One last point, you have to be careful in looking at the results from
experiments like this in irb/ruby programs because some of the
reflection methods in ruby deliberately lie about what's really
happening under the covers.
 
R

Robert Klemme

2007/12/17 said:
Yes I copied the classname' notation from the diagrams Dave Thomas
uses in the pickaxe.

The statement I wanted to correct, maybe I misunderstood it was:


I read object as an instance of "its class." The above statement is
wrong in two regards.

But note that any class is also an instance.
First, the metaclass isn't on the inheritance chain of the instance,
it's on the inheritance chain of the class, but that's a horse of a
different color.

Again, since a class is also an instance I choose to use the more
general term. But the statement holds true for classes as well because
with regard to this there is no difference between an ordinary
instance and an instance of Class (or Module for that matter). There
is in fact some difference in behavior that results from this (see my
posting) but the singleton class of a class sits between the instance
(the class) and its class (Class).
Second, in the klass chain it's on the other side of the class, it goes:

instance - klass-> class -klass-> class'

NOT

instance -klass-> class' -klass-> class

Do I understand you properly that you claim the chain is

Array -> Class -> Array'

If so, I strongly believe you are wrong. It's

Array -> Array' -> Class

Or did you mean

anArray -> Array -> Array'

Where Array' is the singleton class of Array like in

irb(main):001:0> c=class <<Array;self;end
=> #<Class:Array>

This is correct.
Generally singleton classes of class instances and of ordinary instances
behave the same. The only difference I can see so far is that, since
classes can inherit from each other, a subclass also shares instance
methods defined for its superclass (see my irb session). Btw, something
similar happens with instances when cloned:

irb(main):001:0> o = Object.new
=> #<Object:0x7ff9e474>
irb(main):002:0> def o.xxx;1;end
=> nil
irb(main):003:0> o.xxx
=> 1
irb(main):004:0> class <<o;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):005:0> o1 = o.clone
=> #<Object:0x7ff80eec>
irb(main):006:0> o1.xxx
=> 1
irb(main):007:0> class <<o1;self;end.instance_methods.grep /xx/
=> ["xxx"]
irb(main):008:0> o2 = o.dup
=> #<Object:0x7ff73cec>
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx' for #<Object:0x7ff73cec>
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Not quite the same, what happens here is that cloning an object with a

That's why I wrote "similar" and not "same"...
singleton class gives the clone a singleton class which looks like the
original object's singleton class:
One last point, you have to be careful in looking at the results from
experiments like this in irb/ruby programs because some of the
reflection methods in ruby deliberately lie about what's really
happening under the covers.

Can you elaborate which methods do actually lie?

Cheers

robert
 
R

Rick DeNatale

2007/12/17, Rick DeNatale <[email protected]>:

But note that any class is also an instance.

But that doesn't seem to be relevant to the 'slightly restated assertion that.

Inheritance wise the metaclass sits between an object and its class.

Let's say that the object is an array, we have


array -klass-> Array ->klass-> Array' (i.e. Arrays metaclass)

which isn't an inheritance chain anyway, but it illustrates that the
metaclass doesn't sit between an instance and its class. Now lets'
say the object was a class, say Array

we have two parallel inheritance chains:

Array Array'
| |
super super
V V
Object Object'
Again, since a class is also an instance I choose to use the more
general term. But the statement holds true for classes as well because
with regard to this there is no difference between an ordinary
instance and an instance of Class (or Module for that matter). There
is in fact some difference in behavior that results from this (see my
posting) but the singleton class of a class sits between the instance
(the class) and its class (Class).

Yes, but not all singleton classes are metaclasses, I maintain that
only singleton classes of classes are properly called metaclasses.
The meaning of metaclass is "class of a class", so calling the
singleton class of a non-class object a metaclass is a misnomer,
albeit, sadly, a common one.

Given this definition, it only holds true if the object in the general
statement is a class which makes the statement incorrect in general.
That's why I wrote "similar" and not "same"...

The point is that there's no inheritance involved, just copying.

Can you elaborate which methods do actually lie?

Off the top of my head, ancestors for one.
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top