Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby eigenclass unexpected behaviour

First, let's add a method to retrieve eigenclass "copied from this blog post"

class Object 
  def eigenclass 
    class << self
      self
    end 
  end 
end

Then create a simple class

class A
end

puts A.new.eigenclass.superclass      # => A
puts Class.new.eigenclass.superclass  # => #<Class:Object>

I was expecting the second puts to output Class

Any clue why this happened?

like image 238
bazaretas Avatar asked Dec 25 '22 08:12

bazaretas


2 Answers

By puts A.new.eigenclass.superclass, you are effectively calling #eigenclass on the instance of class A. I will begin with backstory to explain how eigenclass actually works, and will then proceed to tell what is happening in your code.

BackStory:

EigenClass is a hidden class which contains the singleton methods available for that specific object only.

So for obj = Foo.new, the class hierarchy actually looks like:

obj --eigenclass--> #> --(superclass)--> A

instead of:

obj --(class)--> A

A hidden class can be produced after you hijacked the self with #eigenclass.

Now, in Ruby, Class is an object. This also means that #eigenclass should show the the hidden eigenclass of A too (where A's sigleton methods are kept).

A --(eigenclass)--> # --(superclass)--> #

Now the reason why it shows # instead of A is because Ruby organizes the classes, superclasses and eigenclasses in a very beautiful pattern. This can be shown with example instead of quoting it in confusing words:

A.superclass #=> Object   
A.eigenclass #=> #<Class: A>   
A.eigenclass.superclass #=> #<Class: Object> => Eigenclass of Object   
A.eigenclass.superclass == Object.eigenclass #=> true   

The superclass of an eigenclass of a class is the eigenclass of the superclass of the original class.

Now, coming to your case: Class.new.eigenclass.superclass, this is self-explanatory now. Class.new corresponds to a new anonymous class, say B, and you are effectively calling eigenclass.superclass on it. Since the superclass of B is Object, the superclass of eigenclass of B is the eigenclass of superclass of B.

Tried my best to explain with examples. Please feel free to clarify it further in comments below; will update the answer accordingly. Complementary(from Pragmatic MR): Shot from Pragmatic MR.

In the figure shown above, D inherits from C. So D.eigenclass.superclass is the eigneclass of (superclass of D)[which is C]. Now C's superclass is Object.. and so is the same logic.

Regards

like image 144
kiddorails Avatar answered Dec 27 '22 22:12

kiddorails


From that blogpost, you can construct a similar diagram:

                        +------------------+               +-------------------+
                        |      Object      |- eigenclass ->| Object eigenclass |
                        +------------------+               +-------------------+
                                 ^                                   ^             
                                 | superclass             superclass |                                                     
                        +------------------+               +-------------------+
                        |        A         |- eigenclass ->|    A eigenclass   |
                        +------------------+               +-------------------+
                                 ^
                                 | superclass
+-------+               +------------------+                                   
| A.new |- eigenclass ->| A.new.eigenclass |                                   
+-------+               +------------------+    

Trying to find the superclass of the eigenclass of an instance of A shows that it points to the A class.

A.new.eigenclass.superclass      # => A                               

Class.new returns an instance of a Class object, i.e. a new class. It is a class, just like the A class.

Class.new.class # => Class
A.class         # => Class

A's superclass and Class.new's superclass are both implicitly Object.

Class.new.superclass # => Object
A.superclass         # => Object

Because A's superclass is Object, A's eigenclass's superclass is Object's eigenclass.

Object.eigenclass                            # => #<Class:Object>
A.eigenclass.superclass                      # => #<Class:Object>
A.eigenclass.superclass == Object.eigenclass # => true

Similarly, finding the superclass of the eigenclass of Class.new yields Object's eigenclass

Class.new.eigenclass.superclass              # => #<Class:Object>

The difference between Class.new and A.new is that Class.new is itself a class and so can construct new objects, while A.new cannot.

Class.new.new # => #<#<Class:0x007f86b50d8f70>:0x007f86b50d8f20>
A.new.new     # => NoMethodError: undefined method `new' for #<A:0x007f86b50cbf50>
like image 42
hjing Avatar answered Dec 27 '22 22:12

hjing