9

如果你创建一个类:

class Foo { }

该类将继承其所有方法Any,然后Mu

我想创建一个不从任何其他类继承的类:它应该包含一个FALLBACK方法,该方法应该捕获对该对象实例的所有方法调用。

我查看了MetaModel代码,但似乎没有一种简单的方法可以实现这一目标。欢迎所有建议!

更新:我决定按照Jonathan Worthington 的描述截取任何方法调用方式。这导致了 CPAN 上的两个新 Perl 6 模块:InterceptAllMethodsObject::Trampoline

4

2 回答 2

9

这是可能的,尽管您可能会遇到需要进一步努力的实际问题。调用构造逻辑是评论中已经指出的一个很好的例子。除此之外,一切都有望成功地对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实现。

于 2018-06-25T23:58:57.883 回答
7

这是另一个想法:您可以创建一个新的元类,它继承自ClassHOW,但会覆盖角色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.

于 2018-06-26T07:41:04.267 回答