TL; DR Я опишу несколько вопросов. В конце я покажу решение, которое компилируется и работает на недавнем (2020) Rakudo. Это простой вариант вашего собственного кода, но я не достаточно осведомлен, чтобы ручаться за его правильность, не говоря уже о целесообразности [1] [2] .
Cannot change the type of a Any type object
Сообщение об ошибке поступает из строки rebless
:
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
A $
как термин [3] не означает self
, но вместо этого анонимное состояние Scalar
переменная . По умолчанию он содержит Any
, отсюда и сообщение об ошибке. Это должно быть self
. [4]
Исправив эту первую проблему, мы получаем новую, в зависимости от используемой версии Rakudo:
Как и первое сообщение об ошибке, которое мы только что исправили, эти два также запускаются оператором rebless
. [5]
Мы должны решить обе проблемы.
В более новом Rakudo, исправления проблем Cannot change the type of a Any type object
и not a mixin type
недостаточно, если я использую ваш код "добавления нового миксина"; Я просто получаю ошибку Incompatible MROs
.
И наоборот, использование альтернативного кода, исправляющего проблему Incompatible MROs
на старом Rakudo, приводит к not a mixin type
, если , что проблема не решена должным образом. (В моей первоначальной версии этого ответа я решил проблему Incompatible MROs
, а затем пренебрег тестом на более новом Rakudo!)
Ваш диагноз ошибки Incompatible MROs
был "Этот явно не проходит, так как Adult
- это миксин на Person
, а не на Child
". Я прочитал это, взглянул на код, поверил тебе и пошел дальше. Но потом я вернулся к той же проблеме, используя код, который вы написали, чтобы попытаться решить ее. Что дает?
Исходя из моих экспериментов, кажется, что не только класс "to" (класс которого должен быть новым классом объекта, подлежащего анализу) должен иметь MRO, который совместим с объектом, который подвергается анализу. в соответствии с вещами, которые я ожидал (например, наследование классов), но также объект "from" (подлежащий обработке) не может быть оба :
(я не знаю, является ли это ошибкой, которая может быть исправлено или неизбежным ограничением. Я знаю, что недавнее (2020) Rakudo имеет это ограничение, используя оба варианта кода, предложенного Джонатаном в предыдущем SO.)
Это означает, что «добавление нового миксина, чтобы избежать проблема циклической ссылки "(" заглушки и наследование не нравятся друг другу ") не решает вашу проблему.
Вместо этого я вернулся к вашей попытке" всего один класс и один миксин "(которая закончилась с Illegally post-declared type
в течение м, вы изначально написали это) и попробовали другой подход, чтобы обойти эту ошибку.
Следующий вариант вашего кода "просто один класс и один миксин" работает на Rakudo v2020.01.114. gcfe.2.cd c .56. Все, что я сделал, превратил константу Adult
в переменную. Я написал ...
для остального кода, который совпадает с вашим кодом:
my $Adult;
...
Metamodel::Primitives.rebless(self, $Adult) if $.age == 18;
...
$Adult = Child but role { method can-vote { True } }
$Adult.^set_name('Adult');
...
Hth.
Сноски
[1 ] Решение Джонатана в недавнем SO использовало конструкции времени компиляции для Adult
. Мое решение следует примеру Джонатана, за исключением того, что оно строит мишень для рефлекса $Adult
в время выполнения . Я не уверен, что это технически безопасно перед лицом новой оптимизации, представленной @JonathanWorthington. Я постараюсь «призвать» его прокомментировать.
[2] Кроме этой сноски, мой ответ не касается мудрости использования rebless
. Два вопроса сразу приходят мне на ум. Во-первых, это надежная функциональность с учетом турофилии , которая, несомненно, является центральной для вас, даже если вам нужно спросить ваших последних SO. (И с этим, metaturophilia. То есть, у нас есть дыры в нашем подходе к созреванию Raku, языка, и Rakudo, реализации. Поскольку код степени, написанный одним из нас, приводит к заполнению пробелов, мы все можем быть благодарны.) Во-вторых, это надежная документация СС, учитывая, что (насколько я могу судить) некоторая ключевая документация нарушает общее правило ограничивать себя спецификацией Raku согласно roast и вместо этого "в значительной степени отражает систему метаобъектов, реализованную компилятором Rakudo Raku" . Я просто исправляю ошибки до тех пор, пока ваш код не скомпилируется и не запустится в версии Rakudo 2020 года.
[3] См. Что такое термин? в связи с некоторый контекст в этом комментарии .
[4] Некоторые люди могут предположить, что если $.foo
является .foo
из self
, то $
должен быть self
. Такое мышление было бы разумной презумпцией, если бы у raku был типичный контекстно-свободный токенизация, используемый для большинства языков программирования. Более того, оно , как правило, относится и к коду Raku, так же, как это обычно применяется даже на естественном языке. (Если за маркером Engli sh «my» следует «self», то это, вероятно, означает то же самое, что и «я».) Но грамматика Raku сознательно сочетает чувствительность к контексту , синтаксический анализ без сканирования и maxim munch для поддержки создания языков с более естественным чувством, чем это характерно для языков программирования. И здесь мы видим пример. В «термине положение» [3] вход $.foo
распознается как один токен вместо двух ($
, за которым следует .foo
), тогда как вход $,...
распознается как два токена ( $
, за которым следует оператор разделителя списка ,
), а не один.
[5] Все эти сообщения об ошибках генерируются в частях Rakudo, которые находятся близко к металлу. Если вы используете MoarVM в качестве бэкэнда, они приходят из файла P6opaque. c.