Как создать класс, который не наследуется от любого другого класса?

если вы создаете класс:

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.