Догоняя 'require' для класса метамодели

Я определил собственный класс метамодели для создания особого вида классов. Теперь я хотел бы, чтобы эти классы автоматически регистрировали себя с помощью специального менеджера. В принципе, это было бы так (compose вызываться каждый раз, когда загружается модуль класса):

use MyManager;

class MyHOW is Metamodel::ClassHOW {
    method compose ( Mu \type ) {
        self.add_parent( type, MyParentClass );
        callsame;
        registerMyClass( type );
    }
}

Тогда у меня есть что-то вроде:

use v6;
use MyClass;
myclass Foo { ... }

в модуле. Затем есть объект менеджера, который сканирует репозитории / файловую систему и requires модули с именами, соответствующими определенному шаблону. После этого нужно знать, что myclassОни определены в каждом модуле. Он может сканировать таблицу символов загруженного модуля. Но это не будет работать, если загруженный файл содержит несколько модулей или вообще не содержит модулей - как в примере выше.

Пока это выглядит как INIT Фазер предоставил бы решение, но я изо всех сил пытаюсь найти, как получить блок тела класса изнутри composer метод.

1 ответ

Решение

При выполнении метапрограммирования методы метаобъекта вызываются во время компиляции, так как объявления анализируются. Следовательно compose метод вызывается сразу после разбора myclass foo { } декларация. Результат компиляции модуля затем сохраняется, и ничто в метаобъекте не будет снова обрабатываться при загрузке модуля.

Я не знаю поддерживаемого способа внедрить обратный вызов времени загрузки в модуль, где объявлен тип. Тем не менее, можно установить символы в отдельный пакет - используемый в качестве реестра - и затем найти их там.

Например, учитывая, что у меня есть lib/MyClass.pm6 это выглядит так:

package MyRegistry { }

class MyParentClass { }

class MyHOW is Metamodel::ClassHOW {
    method compose ( Mu \type ) {
        MyRegistry::{self.name(type)} = type;
        self.add_parent( type, MyParentClass );
        callsame;
    }
}

my package EXPORTHOW {
    package DECLARE {
        constant myclass = MyHOW;
    }
}

И я пишу некоторые файлы mods/A.pm6 а также mods/B.pm6 как это:

use MyClass;
myclass A { }

И это:

use MyClass;
myclass B { }

Затем, когда я требую их в сценарии, как это, и сбросить ключи в MyRegistry, они оба будут зарегистрированы там:

use MyClass;
for dir('mods', test => /pm6$/) {
    require $_;
}
dd MyRegistry.WHO.values;

Таким образом, давая предсказуемый способ найти их всех.

Обратите внимание, что для работы подобной техники вам действительно нужно сохранить их в Stash, поскольку загрузчик знает, как объединить эти символы, тогда как другие типы, по-разному затронутые во время компиляции разных модулей, приведут к конфликтам во время загрузки.

Перед вами стоит небольшая задача - убедиться, что все установлено под достаточно уникальным ключом; имя типа, которое я использовал здесь, вероятно, не достаточно уникально в общем. Вероятно, я бы просто сгенерировал что-то достаточно случайное, чтобы вероятность столкновения была крайне маловероятной.

Другие вопросы по тегам