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

3

86

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?

Rowdy answered 27/10, 2009 at 13:31 Comment(0)
C
124

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.
Combes answered 27/10, 2009 at 13:59 Comment(7)
So then, each instance is an anonymous subclass of the created class?Rowdy
Each instance's class is an anonymous subclass of the created class. f1's class is anonymous subclass of Foo, Foo's class is an anonymous subclass of Class.Combes
nice answer :) a lot of people do not understand this as clearly as you do.Gandy
so f1's class is NOT Foo, but rather an anonymous sub-class of Foo? But, when I call a method on f1, doesn't it look in Foo to find that method? or Does it look in the anonymous sub-class of Foo and then in Foo?Skees
@Skees exactly, it's the same as any other subclass relationship. First it looks in the eigenclass, then it looks in Foo, then it looks in Foo's superclasses if any. Or, conceptually that's how it works; the implementation has some optimizations for the common case, IIRC.Combes
How is the eigenclass of f1 different then, conceptually, from the actual instance of f1. If f1 is the only instance that will ever have access to the methods of its eigenclass, doesn't the distinction between f1 and its eigenclass kind of break down?Contingency
@Contingency Yeah, kinda. The really important distinction is between "Foo" and "f1's eigenclass"; if you've got that, you're probably fine.Combes
P
50

The simplest answer: the eigenclass can't be instantiated.

class F
 def eigen
  class << self 
   self
  end
 end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
Pusan answered 16/11, 2009 at 16:9 Comment(4)
you may have only 1 point on this website, but i like you and your style.Gandy
Agree w/ banister; this is a great answerCordelier
This is an extremely insightful and helpful comment IFF one has already read @DavidSeiler's answer above.Sleep
Theo power here is demo'ing the exception that is raised.Lepidosiren
J
11

Yehuda Katz does a pretty good job of explaining the subtleties in "Metaprogramming in Ruby: It's All About the Self"

Jamboree answered 21/12, 2009 at 1:38 Comment(1)
subtitles or... subtleties? ;)Gandy

© 2022 - 2024 — McMap. All rights reserved.