Words and Code

One writer’s journey from words to code.

Class Inheritance: Part 2 (Singletons and Eigens, Oh My!)

This blog post is part of a series on Class Inheritance. Read Part 1 here.

I recently stumbled upon this line of code that totally floored me. I mean, I had to drop everything I was doing and do some serious Googling, my friends. Well, that and also I asked my fellow developers at work who are far more seasoned programmers than me.

Now, I’m going to share this line of code with you, but you’ve got to try not to totally freak out if you haven’t seen it before. Are you ready? Ok, here we go:

1
2
3
4
5
6
7
class SomeObject
  class << self
    def some_method
      "A string returned by some_method."
    end
  end
end

Are you with me? Did you make it past the second line? Do I need to call an ambulance? Well, if your reaction was anything like mine, you saw class << self and experienced something akin to an aneurysm.

Ok, let’s agree on one thing right here, right now: everything in Ruby is an object. No matter how crazy it’s about to get, just remember that. In fact, it’s worth repeating again: Everything in Ruby is an object – even a class.

Got it? Okay, now let’s figure what the hell that code means, exactly.

Class Methods, Because Your Instances Should Never Be Too Greedy

In order to understand the meaning behind class << self, we first need to understand the << self syntax. Let’s recap a couple basics about class inheritance first:

  1. Classes can have two different types of methods: class methods, which can be called on a Class object, and instance methods, which can only be called on instances of a Class object.
  2. There are multiple ways to create class methods:

You can use self as the receiver of a method within a class, which is probably the most common way of defining a class method:

1
2
3
4
5
class Book
  def self.type
    "paperback"
  end
end

You can also define a method on the class name (for example, class Book), which implies that the entire Class Book object can receive the type method:

1
2
3
def Book.type
  "paperback"
end

But, you can also do this:

1
2
3
4
5
class << Book
  def type
    "paperback"
  end
end

And – wait for it – this:

1
2
3
4
5
6
7
class Book
  class << self
    def type
      "paperback"
    end
  end
end

Okay, so that funky class << self syntax is just another way of defining a class method? But…but how?

One Method To Rule Them All

So, we know that class methods are a good place to put all the behavior and functionality of all instances of that class. If you want all instances of your Book objects to have a title and an author, you’d want that behavior to be put into the Book class, since all instances of Book will inherit from their parent class.

But, what if you wanted a particular instance of a Book to have a specific kind of functionality. Well, you definitely wouldn’t want to define a method on the class, because it’s only a particular instance that you care about. So why not define a method on that particular instance, then?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
great_gatsby = Book.new
charlottes_web = Book.new

def charlottes_web.wilbur
  "Some Pig"
end

charlottes_web.type
# => "paperback"

charlottes_web.wilbur
# => "Some Pig"

great_gatsby.type
# => "paperback"

great_gatsby.wilbur
# => NoMethodError: undefined method `wilbur' for #<Book:0x007fc8ebb01bc8>

At first glance, nothing here seems all that weird, right? Both the books have access to the type class method, which we defined earlier, while only the charlottes_web Book object has access to the wilbur method. Ruby seems to know that the wilbur method is scoped only to this instance, but it also knows that this instance has other methods – class methods – that are also accessible to it. But how is this actually possible?

Well, the answer is for two reasons: singleton methods and Ruby’s method lookup chain.

Singleton methods are methods defined on an object itself, rather than on an object’s class. Ruby’s method lookup chain is pretty intricate:

An object inherits from its class, and its class can inherit from many more objects, which will then inherit from Object, which inherits from the great grandma of all objects in Ruby: BasicObject. Remember: everything in Ruby is an object – even a class.

So at this point, you’re probably wondering what this has to do with class methods. Well, a lot, actually.

A Class Of One’s Own

We can create all the singleton methods we want, but where do they all go? Not in the Book class, that’s for sure. So where do we put them, exactly?

It turns out, an object’s singleton methods all go into their own neat little class called an eigenclass, which is just another name for a singleton class. The word “eigen” comes from a German term meaning “one’s very own”. I personally like to call it an eigenclass because it minimizes confusion between singleton methods and classes. Also, throwing down terms like eigenclass make you sound like a total badass.

Here’s the cool thing about eigenclasses: they’re totally hidden. I should warn you though, the thing that makes them cool is the very same thing that makes them complicated. Because they’re hidden and anonymous, you obviously can’t see them and so it’s hard to figure out where in the method lookup path they are. Here’s the trick, though: whenever you open up an eigenclass, you shift up the original class.


In my book example, the charlottes_web object inherits from the Book class. But when I created the wilbur method, I opened up the object’s eigenclass, and shifted up the original Book class. Now, the charlottes_web object inherits from its eigenclass, which in turn inherits from the Book class. The Book class has now become the superclass of our object.

Okay, that was a lot. To recap, here’s the most important stuff to know about the mysterious eigenclass:

  1. An eigenclass is an anonymous class that is created to hold an object’s singleton methods.
  2. The anonymous eigenclass then becomes the object’s immediate class, which it inherits from.
  3. The original class is re-designated as the superclass of the anonymous eigenclass.
  4. But because eigenclasses are hidden, when you call charlottes_web.class, it will return Book.

Reveal Thyself, You Eigen, You!

So, eigenclasses don’t show themselves. Cool. Except not cool. Because I want to see it! Well, you can see it, but you have to do a little extra work to make the magic happen. Actually, all you have to do is add a method available to all Ruby Objects, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Object
  def eigenclass
    class << self
    # This is what opens the eigenclass of the object that is the reciever of the eigenclass method

      self
      # Now we are in the scope of the eigenclass, and "self" is now the
      # eigenclass object. Since we are returning self, we will get to see what
      # the eigenclass actually looks like!

    end
  end
end

We open up the eigenclass of the object when we use the class << self syntax. And once we open it up, we are in the scope of the eigenclass. Here, self is now the eigenclass object (everything in Ruby is an object!), and since we are returning self, we will get to see what the eigenclass actually looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
charlottes_web.eigenclass
 => #<Class:#<Book:0x007fc8ebaf3c08>>
charlottes_web.eigenclass.class
 => Class
charlottes_web.eigenclass.superclass
 => Book
charlottes_web.eigenclass.superclass.superclass
 => Object
charlottes_web.eigenclass.superclass.superclass.superclass
 => BasicObject
charlottes_web.eigenclass.superclass.superclass.superclass.superclass
 => nil

charlottes_web.singleton_methods
 => [:wilbur]
charlottes_web.eigenclass.singleton_methods
 => [:type]

Interesting. So, the eigenclass of our charlottes_web object does look pretty anonymous, and you might not even notice its weirdness at first. But, it does clearly seem to be a Class object and, as we expected, was inserted into the method lookup chain right between the object and the Book class.

But if you look at the last two lines and their value, that’s where stuff gets really interesting. The wilbur method, which we defined on a particular instance of an object, is a singleton method on only that object. The type method, however, lives in the eigenclass of that object.

Is it all coming together now? While the wilbur method could only be called on an instance, the type method can be called on an entire class. So, perhaps you’re not not technically creating a class method, but more of an “eigenclass class method”. In fact, there actually is no such thing as a “class method” – you’re actually inside of the eigenclass when you create that. But effectively, it’s the same thing, because you can’t see an eigenclass and it still inherits all the methods from a basic Class object.

WAT


If all of this was Greek to you, don’t worry. This is just honestly really hard stuff. At the end of the day, all you really need to know is that class << self is just another way of defining a class method. That and, everything in Ruby is an object.

There’s plenty of debate about whether or not using the class << self syntax is helpful or just confusing. Personally, I like to use it when I have a lot of class methods to define. It can keep your code clean and easy to read, and helps you avoid typing out self.method multiple times.

But, if DRYing out your code is more confusing to you than helpful, you should probably avoid it. It’s more about personal preference than anything else. Find a style that works for you and try not to think too much about all the crazy anonymous classes spinning around your head. Try being the keyword here.

tl;dr?

  • Technically speaking, there’s actually no such thing as a class method in Ruby. You’re always opening up the eigenclass/singleton class of an object whenever you define a class method. Keep in mind that there are many different syntaxes you can use in order to achieve this!
  • Curious about the self.method vs class << self debate? Check out this thread on Stack Overflow.
  • Read more about the method lookup path to understand where Ruby looks for stuff.
  • Tons of good resources on this stuff here and here. Oh, and here.