В Ruby класс является объектом. Окей. Тогда у этого класса должен быть класс, который этот класс создал.
Выходит, что этот класс -- Class
class Viktor
end
puts Viktor.class.name # => "Class"
Но дальше самое интересное
puts Class.class.name # => "Class"
irb(main):020:0> Class === Class.class
=> true
irb(main):021:0> Class === Class.class.class
=> true
Т.е. класс, как объект, создаётся сам собой.
Пожалуйста, объясните мне природу этого явления. Является ли он синглтоном?
Ответ
Нашёл на английском SO ответ на схожий вопрос. Приведу здесь вольный перевод.
Как и в большинстве других языков, в Ruby есть некоторые базовые экземпляры, существование который изначально заложено в языке. Они спустились с небес, материализовались из тонких материй, появились на свет магическим образом.
Вот список этих магических вещей:
Object не имеет суперкласса, но вы не можете определить объект без суперкласса, прямым суперклассом (пусть и не всегда явно) является Object. [Замечание: суперкласс для Object можно задать, но в конце концов, всегда будет объект, у которого не окажется суперкласса.]
Object является экземпляром класса Class, который является субклассом Object (что значит, Object косвенно является экземпляром самого Object)
Class является субклассом Module, который является экземпляром Class
Class является экземпляром Class
Ничто из этих вещей нельзя объяснить в Ruby.
BasicObject, Object, Module and Class -- все они должны быть созданы в один и тот же момент, потому что они имеют цикличную зависимость друг от друга.
Тот факт, что подобные зависимости не могут быть объяснены на языке Ruby, не значит, что спецификации языка Ruby не могут сказать, что так должно быть. Это уже задача исполнителя: разобраться с тем, как это осуществить. В конце концов, Ruby-исполнитель имеет тот доступ к объектам, которого у программиста попросту нет.
Например, Ruby-исполнитель для начала может создать BasicObject, задав указатели его superclass и class равными null
Затем создать Object, задав superclass равным BasicObject и class равным null
Затем создать Module, задав superclass равным Object и class равным null
Наконец, создать Class, задав его superclass равным Module и class равным null
Теперь переназначить указатели на Class для BasicObject Object,ModuleиClassравнымиclass`.
Всё это довольно легко проделать извне, но трудно представить, как бы осуществить это изнутри.
Как только они начали существовать, совершенно возможно осуществить большую часть их функционала в чистом Ruby. Вам только нужно иметь голый скелет этих классов, т.к. благодаря открытым классам Ruby, мы можем дополнять их функционал в любое время.
Выполнение class Class не создаёт нового класса с именем Class, а открывает уже существующий класс Class, который уже был нам предоставлен средой исполнения.
Так что весьма просто представить стандартное поведение Class#new на чистом Ruby:
class Class
def new(*args, &block)
obj = allocate # another magic thing that cannot be explained in Ruby
obj.initialize(*args, &block)
return obj
end
end
[Замечание: на самом деле, initialize -- приватный метод, так что вам нужно использовать obj.send(:initialize, *args, &block), чтобы обойти ограничения доступа.]
Кстати, Class#allocate -- ещё один магический трюк. Он выделяет новый пустой объект в пространстве объектов Ruby, что не может быть осуществлено средствами самого Ruby. Так что Class#allocate -- это
что-то, что должно быть также предоставлено средой исполнения.
От себя добавлю, что в исходном коде RubyMine (rubystubs23/class.rb) можно найти комментарий, довольно доходно всё объясняющий:
# Classes, modules, and objects are interrelated. In the diagram
# that follows, the vertical arrows represent inheritance, and the
# parentheses metaclasses. All metaclasses are instances
# of the class `Class'.
# +---------+ +-...
# | | |
# BasicObject-----|-->(BasicObject)-------|-...
# ^ | ^ |
# | | | |
# Object---------|----->(Object)---------|-...
# ^ | ^ |
# | | | |
# +-------+ | +--------+ |
# | | | | | |
# | Module-|---------|--->(Module)-|-...
# | ^ | | ^ |
# | | | | | |
# | Class-|---------|---->(Class)-|-...
# | ^ | | ^ |
# | +---+ | +----+
# | |
# obj--->OtherClass---------->(OtherClass)-----------...
Приблизительный перевод комментария над диаграммой:
Классы, объекты и модули взаимосвязаны. В диаграмме ниже вертикальные
стрелки означают наследование, а скобочки -- метакласс. Все метаклассы
являются экземплярами класса Class