Как создать класс, который не наследуется от любого другого класса?
если вы создаете класс:
class Foo { }
класс унаследует все свои методы от Any
, а потом Mu
.
Я хочу создать класс, который не наследует от любого другого класса: он должен содержать один элемент FALLBACK
метод, который должен поймать все метод вызывает экземпляры этого объекта.
Я просмотрел MetaModel
код, но, похоже, нет простого способа достичь этой цели. Все предложения добро пожаловать!
обновление: я решил пойти перехватить любой вызов метода путь, как описано Джонатаном Уортингтоном. Это привело к появлению двух новых модулей Perl 6 на CPAN: InterceptAllMethods и Объект::Батут.
2 ответов
это возможно, хотя вы, вероятно, столкнуться с практическими проблемами, которые потребуют дальнейших усилий. Вызов логики построения-хороший пример, уже отмеченный в комментарии. Кроме того, ожидается, что все успешно наберет check против Mu
; такие проверки устраняются в большинстве мест как оптимизация, но не другие, и поэтому вы можете ожидать, что столкнетесь с ошибками проверки разных типов.
С этим в стороне, вот как это сделать. Сначала создайте модуль который экспортирует новый мета-тип для class
.
class RootHOW is Metamodel::ClassHOW {
method has_default_parent_type(|) { False }
}
package EXPORTHOW {
constant class = RootHOW;
}
метамодель должна каким-то образом использоваться для настройки Mu
введите в первую очередь, и поэтому здесь мы (ab)используем механизм, который обычно означает "нет, пока нет родительского типа по умолчанию, потому что мы не загрузили нашу объектную модель так далеко". Вставьте это в модуль, скажем, под названием Parentless
, и тогда можно сделать так:
use Parentless;
class NotAMu {
method FALLBACK($name, |c) {
say "called $name with {c.perl}"
}
}
NotAMu.new
выходы:
called new with \()
если ваша цель просто Конст метод отправки, есть гораздо менее разрушительный способ, который не связывается с системой типов. На данный момент ему нужен пользовательский метакласс, который отключает публикацию кэша методов:
class InterceptHOW is Metamodel::ClassHOW {
method publish_method_cache(|) { }
}
package EXPORTHOW {
constant class = InterceptHOW;
}
вы можете написать:
use InterceptAllTheMethods;
class InterceptThemAll {
method ^find_method(Mu $obj, Str $name) {
return -> | { say "calling $name" }
}
}
InterceptThemAll.new
обратите внимание, что в отличие от FALLBACK
, здесь вы возвращаете объект кода, который затем будет вызван. Вы можете написать это find_method
реализация в метаклассе тоже, что может быть лучшим факторингом; трудно сказать без зная проблемы.
этот подход не вызовет никаких проблем, связанных с проверкой типа, позволит вам перехватывать каждую отправку метода, и достаточно легко искать такие вещи, как bless
и просто передать их в Mu
реализация.
вот еще одна идея: вы можете создать новый мета-класс, который наследуется от ClassHOW
, но переопределяет методы, которые role Perl6::Metamodel::MROBasedMethodDispatch
предоставляет версии, которые пропускают все родительские классы.
например, так:
# Maybe this belongs on a role. Also, may be worth memoizing.
method can($obj, $name) {
my @meths;
my %smt := self.submethod_table($obj);
if nqp::existskey(%smt, $name) {
@meths.push(%smt{$name});
}
for self.mro($obj) {
my %mt := $_.HOW.method_table($_);
if nqp::existskey(%mt, $name) {
@meths.push(%mt{$name})
}
}
@meths
}
станет
method can($obj, $name) {
my @meths;
my %smt := self.submethod_table($obj);
if nqp::existskey(%smt, $name) {
@meths.push(%smt{$name});
}
@meths
}
таким образом, вы не столкнетесь с проблемами с кодом, который ожидает, что все типы будут соответствовать Mu
, но вы все еще можете избежать случайного вызова методов из Mu
.