1

There appears to be a contrast between singleton classes of nil, false, true and that of an instance of a custom made class.

i) The singleton class of nil, false, and true are referred to by their assigned constant names:

nil.singleton_class #=> NilClass
false.singleton_class #=> FalseClass
true.singleton_class #=> TrueClass

ii) The singleton class of nil, false, and true appear on ancestor lists:

nil.class.ancestors #=> [NilClass, Object, Kernel, BasicObject]
false.class.ancestors #=> [FalseClass, Object, Kernel, BasicObject]
true.class.ancestors #=> [TrueClass, Object, Kernel, BasicObject]

For the singleton class named AClass of an instance a of a custom class A,

class A; end
a = A.new
AClass = a.singleton_class

i) AClass is not referred to by its assigned constant name:

a.singleton_class #=> #<Class:#<A:0x00007fda832a7eb0>>

ii) AClass does not appear on the ancestor list:

a.class.ancestors #=> [A, Object, Kernel, BasicObject]

Is this the expected behavior? What divides nil, false, true on the one hand and a on the other? What does this specification follow from?

sawa
  • 165,429
  • 45
  • 277
  • 381

4 Answers4

2

Not sure if this helps, but from the docs for singleton_class:

If obj is nil, true, or false, it returns NilClass, TrueClass, or FalseClass, respectively.

So singleton_class has a special behaviour for nil, true and false instead of the standard behaviour of returning the objects's singleton class.

mikej
  • 65,295
  • 17
  • 152
  • 131
  • The object returned (`NilClass`, `TrueClass`, `FalseClass`) is not special as they are indeed the singleton classes. What is special is the way they are displayed. – sawa Feb 28 '19 at 11:06
  • This can be achieved by overriding the `#inspect` method of the singleton class. `class AClass; def self.inspect; 'AClass'; end; end; a.singleton_class #=> AClass` – 3limin4t0r Feb 28 '19 at 11:22
  • @3limin4t0r With non-singleton classes, you don't need to override `inspect` to do that. The question I am asking is, why I need to override it with singleton classes except for `NilClass`, `FalseClass`, and `TrueClass`. – sawa Feb 28 '19 at 11:39
  • 1
    @sawa thanks for your comment. I wonder why those 3 are explicitly mentioned in the documentation if there's no special behaviour going on. It's an interesting question and I'll definitely be following for answers from others. – mikej Mar 01 '19 at 13:23
2

1. nil, false, true objects

i) The singleton class of nil, false, and true are referred to by their assigned constant names:

This is not correct. The singleton_class method of nil, true and false does not return their singleton classes since they don't exist. Instead, it returns their actual classes. The reason why those objects don't have singleton classes is obvious and is well described in the @zeitnot's answer.

This behaviour is expected and is documented here https://ruby-doc.com/core/Object.html#method-i-singleton_class

ii) The singleton class of nil, false, and true appear on ancestor lists

This is not correct too. Since those objects don't have singleton classes, what you see in the ancestors list is their actual classes. But even if they had singleton classes, you wouldn't get them by that way (continue reading to know why)

2. an instance a of a custom class A

i) AClass is not referred to by its assigned constant name:

AClass = a.singleton_class

This code makes AClass refer to a.singleton_class. But it is one-direction relation (from AClass to a.singleton_class). It doesn't make a.singleton_class refer to and return AClass as you are expecting.

ii) AClass does not appear on the ancestor list:

To get an ancestors list including the singleton class execute a.singleton_class.ancestors instead of a.class.ancestors.

a.singleton_class.ancestors #=> [#<Class:#<A:0x000000000191dba0>>, A, Object, Kernel, BasicObject]

The ancestors method only looks up the hierarchy:

class A; end
class B < A; end
class C < B; end

A.ancestors # => [A, ...]
B.ancestors # => [B, A, ...]
C.ancestors # => [C, B, A, ...]
chumakoff
  • 6,807
  • 2
  • 23
  • 45
  • If `true` does not have a singleton class as you claim, then on what class is the following singleton method `foo` defined on? `def true.foo; end; true.method(:foo).owner #=> TrueClass` If you say that is not a singleton class, then what is a singleton class at all? – sawa Feb 28 '19 at 12:29
  • Notice this: `[true, false, nil].all? { |x| x.singleton_class == x.class }`. These constants' classes are returned as exceptional cases when a singleton class is requested (see [here](https://github.com/ruby/ruby/blob/140f8b94ced86966fc6979e8cfc5ef3bfa68da5d/class.c#L1582)). Therefore, your `foo` sits directly on the class of `true` (i.e. `TrueClass`). There is no other object for which `x.class == x.singleton_class` is true. – Amadan Feb 28 '19 at 12:30
  • @sawa You answered your own question. It is `TrueClass` - the actual class. Defining a singleton method for those objects is the same as defining an instance method which belongs to their actual class – chumakoff Feb 28 '19 at 12:33
  • @chumakoff If you say that is not a singleton class, then what is a singleton class at all? – sawa Feb 28 '19 at 12:36
  • @chumakoff I also don't get your argument "But it is one-direction relation (from `AClass` to `a.singleton_class`). It doesn't make `a.singleton_class` refer to and return `AClass` as you are expecting." When you have an unnamed class, its inspection would start to refer to its name when you name it. `b = Class.new => #; B = b #=> B` I don't see how your argument fits that. – sawa Feb 28 '19 at 12:40
  • 1
    What singleton class is is rather well-defined [elsewhere](https://stackoverflow.com/questions/212407/what-exactly-is-the-singleton-class-in-ruby), and I am pretty sure you know what it is. But these three are _specifically instructed to lie about their singleton class_, per my link above, and report their _actual class_ when messaged with `singleton_class`. – Amadan Feb 28 '19 at 12:43
  • 1
    @sawa In the case of singleton class you do not have an unnamed class – chumakoff Feb 28 '19 at 12:44
1

A good example often says more than a lot of words:

require 'singleton'

class FooClass
  include Singleton
  alias singleton_class class

  def inspect
    'foo'
  end
end

Now let's have a look at the similarities:

true #=> true
true.class #=> TrueClass
true.singleton_class #=> TrueClass

foo = FooClass.instance # unfortunately the syntax is not exactly the same
foo #=> foo
foo.class #=> FooClass
foo.singleton_class #=> FooClass

This means that true.singleton_class refers to its own class instead of its singleton class. This makes sense, since true is a singleton which makes its own class the singleton class per definition. (Since it is the class of a singleton.)

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
  • 1
    This quacks alike, but it’s absolutely not how it’s implemented in Ruby. `FooClass.send(:new)` perfectly works, producing another instance of `FooClass`: `[FooClass.send(:new), FooClass.send(:new)].map(&:__id__).reduce(:==) #⇒ false`. – Aleksei Matiushkin Feb 28 '19 at 13:37
  • @AlekseiMatiushkin That's an execution detail. *TrueClass* is written in C while my above class is written in Ruby itself. Reaching the exact same behaviour is pretty difficult. The mindset stays the same though. In the above scenario you're calling a private method on purpose, which is not the provided interface (the public methods). – 3limin4t0r Feb 28 '19 at 13:42
0

In ruby, NilClass, FalseClass and TrueClass can have only one instance. So this is why they do not have a singleton_class.

For Example:

a = nil 
def a.foo
  'foo'
end 

b = nil 
b.foo => # foo

As we might see, b responds to foo method and returns foo string even though we defined foo method for variable a. This means these 3 classes can have only one instance. And there is another thing to point out that nil, false and true are variables but pseudo-varibles. They can not be assigned different data structures. In a sense, they are frozen variables.

zeitnot
  • 1,304
  • 12
  • 28
  • Not sure what you mean by "they do not have a singleton_class." If "they" points to `NilClass`, `FalseClass`, and `TrueClass`, sure they don't have one (although you can create one: `NilClass.singleton_class #=> #`), but not sure what you are trying to say with that. If "they" rather points to `nil`, `false`, and `true`, then you are wrong. They do have singleton classes, i.e., `NilClass`, `FalseClass`, and `TrueClass`, as I have written. And it is also entirely not clear how that follows from the fact that "`NilClass`, `FalseClass`, and `TrueClass` can have only one instance." – sawa Feb 28 '19 at 11:36
  • And it is even harder to find any relevance between the latter part of your answer and my question. – sawa Feb 28 '19 at 11:37