Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between a class and the singleton of that class in Ruby?

Okay, so I'm trying to do some metaprogramming in Ruby and I'm getting a bit confused. According to several articles I've read (like this one), in order to dynamically add class methods to Ruby classes, you have to use the class's singleton class:

class Klass
end

class << Klass
  self.define_method(:foo) { return "foo" }
end

Why is this, and how is that different from this?

class Klass
  self.define_method(:foo) { return "foo" }
end

(Sorry if this question contains any false assumptions. Like I said, I'm a bit confused.)

like image 471
Ajedi32 Avatar asked Jan 16 '23 13:01

Ajedi32


1 Answers

To answer your question directly: Module#define_method creates an instance method. A "class method" is an instance method on the singleton class (or eigenclass) of a Class object. I'm sure that sounds very confusing. Let me explain why Ruby includes the concept of "singleton classes" in the first place:

First, let me say that the basic "framework" of different object-oriented languages are quite varied. Ruby's design as regards objects, classes, metaclasses, etc. is by no means the only possible one, and the language could have been designed in a different way. Having said that, there are logical reasons why Ruby works the way it does. I'll try to explain as concisely as possible...

Think of a simple method call, like:

[1,2,3].first

Here we are calling a method called first, with an Array object as receiver. To process this method call, Ruby needs to search for a matching method definition, and execute it. Where does it start looking? Naturally, in the instance methods of Array. If it doesn't find it there, it will look in Array's superclass, then the superclass of the superclass, as well as Modules which are mixed into Array or its superclasses, etc.

"Class-based" (as opposed to prototype-based) object-oriented languages all work this way, more or less. If you've ever programmed in Java, or C++, or Python, this behavior should be familiar to you.

Now, the creator of Ruby wanted to also make it possible to add methods to just one object. In a prototype-based OO language, that would be easy, but how could it work in a class-based language? He made it possible by introducing the idea of "singleton classes" or "eigenclasses".

A "singleton class" is, simply, a class which has only one instance. I believe that rather than trying to keep track of a different singleton class for every single object, Ruby waits until the first time you try to access an object's singleton class, and then creates the class and inserts it into the object's inheritance chain dynamically.

As I just said, when a method is called, Ruby looks first in the object's class to find a matching definition, then the superclass, etc. Since singleton classes are inserted as the first link in an object's inheritance chain, they are the first place which Ruby will look for a method definition.

Bringing in the concept of "singleton classes" also solved another problem at the same time. In Java (for example), you can define static methods which are called on a class. In Ruby, people often want to do something similar. With "singleton" classes and methods, you can do just that: all you have to do is define a singleton method on a Class object.

(Remember that classes are also objects in Ruby. That's why the concept of "singleton" classes and methods can "kill 2 birds with 1 stone", as I explain above!)

EXTRA INFORMATION:

At the beginning, I mentioned "instance methods". I don't know if that might be confusing, or if you already know what "instance methods" are. When you define a Ruby class, like this...

class MyClass
  def my_method
    # do something
  end
end

...then my_method will be added as an instance method of MyClass. When Ruby searches an object's class, superclass, etc. for a method definition, what it actually looks at are their instance methods. So an object's "methods" are the instance methods of its class, plus the instance methods of the superclass, the superclass of the superclass, etc.

About how singleton classes interact with method lookup in Ruby, there is a slight inconsistency which I didn't mention above. If you want to understand in detail:

Singleton classes of class objects are treated a little differently from singleton classes of other objects in general. If you have a class A which inherits from another class B, and B has singleton methods, A will inherit not just the instance methods, but also the singleton methods of B. In other words, B's singleton class is treated as a superclass of A's singleton class. This is not true of the singleton classes of any other objects.

like image 158
Alex D Avatar answered Jan 25 '23 19:01

Alex D