Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't the eigenclass equivalent to self.class, when it looks so similar?

I've missed the memo somewhere, and I hope you'll explain this to me.

Why is the eigenclass of an object different from self.class?

class Foo   def initialize(symbol)     eigenclass = class << self       self     end     eigenclass.class_eval do       attr_accessor symbol     end   end end 

My train of logic that equates the eigenclass with class.self is rather simple:

class << self is a way of declaring class methods, rather than instance methods. It's a shortcut to def Foo.bar.

So within the reference to the class object, returning self should be identical to self.class. This is because class << self would set self to Foo.class for definition of class methods/attributes.

Am I just confused? Or, is this a sneaky trick of Ruby meta-programming?

like image 387
Robert K Avatar asked Oct 27 '09 13:10

Robert K


1 Answers

class << self is more than just a way of declaring class methods (though it can be used that way). Probably you've seen some usage like:

class Foo   class << self     def a       print "I could also have been defined as def Foo.a."     end   end end 

This works, and is equivalent to def Foo.a, but the way it works is a little subtle. The secret is that self, in that context, refers to the object Foo, whose class is a unique, anonymous subclass of Class. This subclass is called Foo's eigenclass. So def a creates a new method called a in Foo's eigenclass, accessible by the normal method call syntax: Foo.a.

Now let's look at a different example:

str = "abc" other_str = "def"  class << str   def frob     return self + "d"   end end  print str.frob # => "abcd" print other_str.frob # => raises an exception, 'frob' is not defined on other_str 

This example is the same as the last one, though it may be hard to tell at first. frob is defined, not on the String class, but on the eigenclass of str, a unique anonymous subclass of String. So str has a frob method, but instances of String in general do not. We could also have overridden methods of String (very useful in certain tricky testing scenarios).

Now we're equipped to understand your original example. Inside Foo's initialize method, self refers not to the class Foo, but to some particular instance of Foo. Its eigenclass is a subclass of Foo, but it is not Foo; it couldn't be, or else the trick we saw in the second example couldn't work. So to continue your example:

f1 = Foo.new(:weasels) f2 = Foo.new(:monkeys)  f1.weasels = 4 # Fine f2.monkeys = 5 # Also ok print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method. 

Hope this helps.

like image 72
David Seiler Avatar answered Nov 16 '22 03:11

David Seiler