What is a singleton class in Ruby?
Asked Answered
I

3

5

I m having trouble understanding the concept of eigenclass or singleton class in ruby. I read a lot that the eigenclass is a class's class. That doesn't make sense to me as for me a class's class is actually Class as all classes are actually instances of the class Class.

Another thing I don't quite get is the following statement: a class method is actually an instance method of the class eigenclass. The eigenclass is accessible this way :

YourClass = Class.new
class << YourClass
  def class_method
  end
end

But if the eigenclass is indeed the YourClass class (that is Class), shouldn't the preceding piece of code open the class Class and add to it the instance method class_method making it accessible to all of its future instances (that is any regular class defined in the future)?

I actually kind of feel that the singleton class is not the same as Class. When you do :

class MyClass
end

MyClass.singleton_class

you get #<Class:MyClass> which is different from the output of MyClass.class => Class

What is that #<Class:MyClass> output ? This has nothing to do with namespace as otherwise there would be two: Class::MyClass...

I'm Looking for a simple and unambiguous explanation of the eigenclass concept in order to clarify my ideas.

Idun answered 22/4, 2020 at 17:23 Comment(6)
Does this answer your question? What exactly is the singleton class in ruby?Misrule
TBH I dont think I still quite grasp it, from what I read in this answer I would not say that " the eigenclass is a class's class". From that answer, I get that the eigenclass is a special class from an object where methods unique to that object live. I thus understand that every object has 2 different classes : it's classic "class" where all the methods common to all objects of this type live and the eigenclass. Moreover that other answer does not explain the #<Class:MyClass> output...Idun
If the very old and well-received answers to that question do not answer it for you then you should state in your question that you've read them and that they do not answer your specific question because your specific question is different in ways x,y, and z. Otherwise, your generic question "What is a singleton class in Ruby?" is indeed answered by What exactly is the singleton class in ruby?.Misrule
@Misrule To be fair, the answer you cite doesn't really explain what "the singleton class of the object" actually is (I'd say that it is rather a pretty thorough explanation of the role that a singleton class plays), and I believe that that is the thrust of the OP's question. As such, I'm not convinced that this question is a duplicate of the one on which you're basing our vote to close.Doggone
@BobRodes, how would you suggest renaming the title of my question then ? Im open to suggestions !Idun
A little hard to do! Seems that you're looking for something a bit more specific, though, about what's going on under the hood than the other question is. In particular, something about why Ruby doesn't assign class methods directly to the Class class, if the eigenclass is really just a reference to the Class object. Perhaps that helps?Doggone
F
13

Singleton classes hold methods that are specific to a single object.

For generic objects, it's a nice-to-have feature. But for classes, it's crucial. Let's start with the objects:

Singleton classes for objects

Instance methods are usually defined in classes. All instances of the same class share the same instance methods. The singleton class sits between the object and its class. It allows each instance to have its own set of methods, independent of the other instances.

If we have two classes, Foo and Bar with 2 instances each a, b and c, d:

class Foo ; end
class Bar ; end

a = Foo.new #=> #<Foo:0x00007fc280963008>
b = Foo.new #=> #<Foo:0x00007f8319016b18>
c = Bar.new #=> #<Bar:0x00007fa66c8d7290>
d = Bar.new #=> #<Bar:0x00007f94d5106ac8>

You would have this class structure: (simplified, excluding modules)

object          singleton class              class    superclass   ...

  a ── #<Class:#<Foo:0x00007fc280963008>> ─┐
                                           ├─ Foo ─┐
  b ── #<Class:#<Foo:0x00007f8319016b18>> ─┘       │
                                                   ├─ Object ── BasicObject
  c ── #<Class:#<Bar:0x00007fa66c8d7290>> ─┐       │
                                           ├─ Bar ─┘
  d ── #<Class:#<Bar:0x00007f94d5106ac8>> ─┘

Ruby creates those singleton classes lazily, for example when calling singleton_class.

So when defining a method a.hello, it is not stored in a's class Foo, but in a's singleton class:

def a.hello
  'hello from a'
end

a.method(:hello).owner
#=> #<Class:#<Foo:0x00007fc280963008>>  <-- a's singleton class

Because of that, b doesn't see that method, even though both are Foo instances:

b.hello #=> NoMethodError: undefined method `hello'

And we can even define a method with the same name for b without interfering with a:

def b.hello
  'hello from b'
end

b.method(:hello).owner
#=> #<Class:#<Foo:0x00007f8319016b18>>  <-- b's singleton class

a.hello #=> "hello from a"
b.hello #=> "hello from b"

We could also define a generic hello in Foo and override it on a per instance level: (you usually don't do that, but it's possible)

class Foo
  def hello
    'hello'
  end
end

def a.hello
  "#{super} from a"
end

def b.hello
  "b says #{super.upcase}!"
end

a.hello #=> "hello from a"
b.hello #=> "b says HELLO!"

c = Foo.new
c.hello #=> "hello"

Singleton classes for classes

The above is especially important for classes. Each class is an instance of Class:

Foo.class #=> Class

Let's say we wanted to have a method Foo.hello, where would we define it?

Instance methods are usually defined in the instance's class, so we could define it in Foo's class:

class Class
  def hello
    'Hello from Foo'
  end
end

Foo.hello
#=> "Hello from Foo"

But that would make the method available to all instances of Class:

Bar.hello
#=> "Hello from Foo"

String.hello
#=> "Hello from Foo"

It would be better to have a place that's exclusive to the Foo instance. And that place is Foo's singleton class:

def Foo.hello
  'Hello from Foo'
end

or

class Foo
  def self.hello       # <-- self is Foo, so this is just "def Foo.hello"
    'hello from Foo'
  end
end

Just like a.hello above, this method is only available to Foo:

Foo.hello #=> "hello from Foo"
Bar.hello #=> NoMethodError

We call these methods class methods, but they are really just instance methods of the singleton class:

Foo.method(:hello).owner
#=> #<Class:Foo>   <-- Foo's singleton class

Foo.method(:hello).unbind == Foo.singleton_class.instance_method(:hello)
#=> true

And if you compare the singleton methods for classes with those for objects, you'll see that they are identical. That's because in Ruby, classes are objects too, and all objects work alike.

Flocculus answered 23/4, 2020 at 7:41 Comment(0)
D
1

I think you're getting a bit off track by confusing the idea of the eigenclass with the idea of a Ruby class instance. They aren't exactly the same; it's more correct to say that Ruby implements class definitions internally as two objects.

This is easily enough demonstrated:

$ irb
> ObjectSpace.count_objects[:T_CLASS]
 => 1285 
> Foo = Class.new { def instance_method; end }
 => Foo 
> ObjectSpace.count_objects[:T_CLASS]
 => 1287

Internally, every object has a klass pointer, which on the surface points to the class the object is an instance of. However, every object (except certain primitives such as Integer, Float and Symbol) also has an eigenclass instance, and this is what the klass pointer actually points to. In the case of classes, then, the klass pointer doesn't actually point to the Class class, but to the singleton object that's part of the class definition, and that contains the method table that holds the class methods.

As the link that anothermh provides explains, the "ordinary" class object's method table contains all the class's instance methods, while the eigenclass object's method table contains all of the class's class methods. This is the mechanism that prevents all of the class's class methods from being accessible to any instance of the Class class.

Now, the eigenclass (eigen is German for "own" in the sense of "my own") is the class of the class, which is why it's also called a metaclass. (Have a look at the source code for Class::new and you will see a call to rb_make_metaclass.)

This is why a call to MyClass.singleton_class returns #Class:MyClass rather than Class as a call to MyClass.class does. This syntax is analogous to p Foo.new returning something like #<Foo:0x00007f9c0d190400>, which is the class and the pointer to the instance. With #Class:MyClass, MyClass is the pointer to the instance. So, this describes a reference to MyClass's metaclass, or the class of MyClass. This is not to be confused with what class MyClass is an instance of, which, of course, is Class.

In case you're curious, the klass pointer in the eigenclass instance actually points to itself. This is demonstrated by the fact that MyClass.singleton_class.singleton_class returns #Class:#Class:MyClass.

For a more comprehensive overview, see Demystifying Ruby Singleton Classes. For a look at what's going on in the source code, see The Ruby Hacking Guide: Chapter 4. Finally, Ruby Under a Microscope is an excellent resource for a comprehensive deep dive into Ruby internals.

[Edited to incorporate some of the discussion in comments]

Doggone answered 23/4, 2020 at 3:16 Comment(10)
"However, if the object is a class definition, the klass pointer points not to the Class class, but to the eigenclass instance." – Actually, that is true for all objects. Some Ruby implementations may, as a private internal optimization, only actually create the singleton class lazily when it is really needed, but conceptually and semantically, the klass of every object is always its singleton class.Willin
@JörgWMittag That's very interesting. I had the feeling that that was true, but couldn't quite dig up anything that would get me to the point where I was sure of it. Can you point me to some resource that spells that out, or perhaps suggest where I can find the relevant source code?Doggone
Well, you can easily check that something like obj = Object.new; def obj.bar; end; obj.bar; obj2 = Object.new; obj2.bar # NoMethodError works for all objects. There is no place for method obj.bar to live than in obj's singleton class. (Actually, there are a few objects that can't have singleton methods, like Symbols, Floats, and Integers. The real reason is that they are not implemented as objects in the runtime, and thus don't even have a class, let alone a singleton class. But for our semantic model, we can still pretend they have a singleton class that is frozen.)Willin
#Class:MyClass is not the same as #Class:#Class:MyClass. The former is the singleton class of MyClass whereas the latter is the singleton class of the singleton class. (hence the different output)Flocculus
@Flocculus I agree with your like comment thus the sentence. Thus the sentence " the klass pointer in the eigenclass instance actually points to itself. This is demonstrated by the fact that MyClass.singleton_class.singleton_class returns #Class:#Class:MyClass." seems to be incorrect.Idun
@JörgWMittag I get from "Ruby Under a Microscope" that some of the simple objects aren't really objects at all, because their values are saved in the VALUE value that normally contains a pointer value to an RObject structure or some generic object structure such as RArray. What I don't get from there is that klass pointers always point to a singleton – most of its diagrams have klass pointing directly to Class. It would seem that this is misleading. Using your example, obj.singleton_methods includes :bar, which strongly suggests a method table in the singleton class.Doggone
@Flocculus Umm...right? Exactly the point I was trying to make? The class of the singleton class of the class is the singleton class.Doggone
@DavidGeismar I dunno. Seems to me to be correct. If x = Foo.new, inspecting x reveals something like #<Foo:0x00007feaa2169010>. ClassType:PointerValue, in other words. Similarly, #Class:MyClass says that an object of type Class points to MyClass. In other words, the class of MyClass. As such, #Class:Class:MyClass means an object of type Class that also points to the class of MyClass. Which is how I conclude that the klass pointer in the eigenclass points to itself. What refutes this position in your mind?Doggone
@Doggone to me, the "points to itself" part sounds as if MyClass.singleton_class and MyClass.singleton_class.singleton_class should return the same object. Maybe I misunderstand that sentence. Could you clarify it?Flocculus
@Flocculus I suppose there is room for different interpretations. As I see it, though, the syntax is ClassType:InstancePointer. So MyClass.singleton_type is a pointer of type Class that points to the instance MyClass. MyClass.singleton_type.singleton_type is a pointer of type Class that points to the instance Class:MyClass, which, of course, is a pointer of type Class that points to the instance MyClass. In other words, the same instance. Et cetera ad infinitum.Doggone
W
0

Eigenclass is no longer a name used in the Ruby world because Ruby officially introduced a method Object#singleton_class in I don't know which version (sorry).

Every object in Ruby, being a "normal" object or a class, or even a singleton class, has its own singleton class.

A singleton class is a class.

Object.new.singleton_class.is_a?(Class)  #=> true

A singleton class has and only has one instance, and it's the object you call singleton_class on. e.g. the only instance of foo.singleton_class is foo.

Ruby allows you to add methods to individual objects.

a = Object.new
b = Object.new

def a.foo
  'foo'
end

a.foo  #=> "foo"
b.foo  #=> raises NoMethodError 

But all instance methods should be defined in a class, so in which class is a.foo defined? The answer is a.singleton_class. Because a.singleton_class only has one instance that is a, the instance method foo can only be called on a, not on b, though they are of the same type.

As for the class's singleton classes, their purpose is to store the "normal classes'" class methods, or if you twist your brain a little, the instance methods that are bound to the individual class instances.

Don't you feel Ruby's object model consistent and beautiful?

Wiatt answered 23/4, 2020 at 5:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.