Недостаток метакласса Ruby
Я понимаю, что все классы в ruby являются экземплярами класса metaclass. И эти "обычные" объекты являются экземплярами этих классов (экземпляры класса метакласса).
Но я продолжаю задаваться вопросом, я имею в виду, что классы являются корнями из объектов, классы сами являются экземплярами класса (называются метаклассом, потому что его экземпляры являются классами). В некоторых блогах я видел некоторое переопределение метода new
класса Class.
Итак, класс ведет себя как класс, но его экземплярами являются классы. Итак, кажется, что у нас есть круг, он выглядит как класс Class - это сам экземпляр.
Мне явно не хватает места. Каково происхождение класса Class?
Вот пример, который меня смущает:
class Class
def new
#something
end
end
Но ключевое слово class
подразумевает экземпляр класса Class. Итак, как это работает?
Ответы
Ответ 1
как это работает
Просто: это не так. Во всяком случае, не в Ruby.
Как и на большинстве других языков, существуют некоторые основные сущности, которые, как предполагается, существуют. Они падают с неба, материализуются из воздуха, волшебным образом появляются.
В Ruby некоторые из этих волшебных вещей:
-
Object
не имеет суперкласса, но вы не можете определить класс без суперкласса, неявный прямой суперкласс всегда Object
. [Примечание: могут быть реализационные суперклассы Object
, но в конечном итоге будет один, у которого нет суперкласса.]
-
Object
- это экземпляр Class
, который является подклассом Object
(что означает, что косвенно Object
является экземпляром самого Object
)
-
Class
является подклассом Module
, который является экземпляром Class
-
Class
- это экземпляр Class
Ни одна из этих вещей не может быть объяснена в Ruby.
BasicObject
, Object
, Module
и Class
все должны существовать spring в то же время, потому что они имеют круговые зависимости.
Просто потому, что это отношение не может быть выражено в коде Ruby, не означает, что спецификация языка Ruby не может сказать, что это должно быть так. Именно разработчику предстоит выяснить способ сделать это. В конце концов, реализация Ruby имеет уровень доступа к объектам, которые вы, как программист, не имеете.
Например, реализация Ruby может сначала создать BasicObject
, установив как его указатель superclass
, так и указатель Class
на null
.
Затем он создает Object
, устанавливая указатель superclass
на BasicObject
и его указатель Class
на null
.
Затем он создает Module
, устанавливая указатель superclass
на Object
и его указатель Class
на null
.
Наконец, он создает Class
, устанавливая указатель superclass
на Module
и его указатель Class
на null
.
Теперь мы можем переписать BasicObject
's, Object
' s, Module
'и Class
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)
, чтобы обойти ограничение доступа.]
BTW: Class#allocate
- еще одна из этих волшебных вещей. Он выделяет новый пустой объект в пространстве объектов Ruby, что не может быть сделано в Ruby. Таким образом, Class#allocate
- это то, что также должно быть обеспечено системой исполнения.
Ответ 2
Да, класс - это сам экземпляр. Это подкласс модуля, который также является экземпляром класса, а Module является подклассом Object, который также является экземпляром класса. Это действительно круто, но это часть основного языка, а не что-то в библиотеке. Сама среда Ruby сама не имеет того же предела, что и вы, или я, когда мы пишем код Ruby.
Я никогда не слышал, чтобы слово "метакласс" использовалось для разговоров о классе. Он вообще не используется в Ruby, но когда он есть, он обычно является синонимом того, что официально называется "одноэлементным классом объекта", что является еще более запутанной темой, чем треугольник Object-Module-Class.
Ответ 3
Существует мета-круглость, заданная ссылкой "поворот". Это встроенная ссылка суперкласса от корневого eigenclass к классу Class
. Это можно выразить через
BasicObject.singleton_class.superclass == Class
Ключ к пониманию карты .class
показывает эту карту как производную от ссылок eigenclass и superclass: для объекта x
, x.class
является первым классом в цепочке суперкласса x
eigenclass. Это можно выразить через
x.class == x.eigenclass.superclass(n)
где eigenclass
является "концептуальным псевдонимом" singleton_class
(устойчив к проблемам с немедленными значениями), y.superclass(i)
означает i
-th суперкласс y
и n
наименьший, так что x.eigenclass.superclass(n)
является классом. Эквивалентно, eigenclasses в цепочке суперкласса x.eigenclass
пропущены (см. rb_class_real, который также показывает, что в MRI даже ссылки superclass
реализованы косвенно – они возникают, пропуская " iclasses" ).
Это приводит к тому, что Class
для каждого класса (а также для каждого eigenclass) постоянно является классом Class
.
Изображение предоставлено этой диаграммой.
Метаклассная путаница имеет 2 основных источника:
-
Smalltalk. Объектная модель Smalltalk-80 содержит концептуальные несоответствия, которые исправляются объектной моделью Ruby. Кроме того, литература Smalltalk использует диалектику в терминологии, которая, к сожалению, недостаточно исправлена в литературе Ruby.
-
Определение метакласса . В настоящее время в определении указано, что метаклассы являются классами классов. Однако для так называемых " неявных метаклассов" (в случае Ruby и Smalltalk-80) гораздо более подходящее определение было бы для метаобъектов классов.
Ответ 4
Хотя это немного устарело, эта статья _why может помочь в понимании поведения. Вы можете найти еще более глубокое погружение в тему в Paolo Perrotta Метапрограммирование Ruby.