对于我正在编写的库,我在HOW上有一个属性,该属性使用handles
特性将由其使用的另一种HOW所完成的各种角色的方法委托给该HOW的实例。我的第一次尝试是这样的(尽管为了使它更容易阅读,这只会处理Metamodel::Naming
):
class ParentHOW does Metamodel::Naming {
method new_type(ParentHOW:_: Str:D :$name!, Str:D :$repr = 'P6opaque' --> Mu) {
my ::?CLASS:D $meta := self.new;
my Mu $type := Metamodel::Primitives.create_type: $meta, $repr;
$meta.set_name: $type, $name;
$type
}
}
class ChildHOW {
has Mu $!parent;
has Mu $!parent_meta handles <set_name shortname set_shortname>;
submethod BUILD(ChildHOW:D: Mu :$parent is raw) {
$!parent := $parent;
$!parent_meta := $parent.HOW;
}
method new_type(ChildHOW:_: Mu :$parent is raw) {
my ::?CLASS:D $meta := self.new: :$parent;
Metamodel::Primitives.create_type: $meta, $parent.REPR
}
method name(ChildHOW:D: Mu \C --> Str:_) { ... }
}
my Mu constant Parent = ParentHOW.new_type: :name<Parent>;
my Mu constant Child = ChildHOW.new_type: :parent(Parent);
say Child.^shortname; # OUTPUT: Parent
问题是,如果我使HOW处理的所有类型中的任何一种都可以随时更改方法,那么它将不再适用于所有方法。因此,我想动态地生成给定该HOW覆盖的方法列表和应处理其方法的类型的列表。由于如何实现handles
特性,这并不像听起来那样容易。例如,这将不起作用:
has Mu $!parent_meta handles do {
my Array[Str:D] constant PARENT_METHOD_OVERRIDES .= new: <name>;
((), Metamodel::Naming)
.reduce({ (|$^methods, |$^role.HOW.methods: $^role) })
.map(*.name)
.grep(PARENT_METHOD_OVERRIDES ∌ *)
};
那么您将如何动态生成特征的值以在这种情况下使用?
特质应用程序是在编译期间设置的,因此我们需要一种方法来生成一个值,供其在编译期间使用。这可以使用BEGIN
移相器完成,但是在这种情况下,最好用constant
编写。
Raku中的常量不仅仅是声明后不能分配或绑定到的变量。通常,在声明变量时,将在编译过程中安装其符号,但是直到运行时才真正设置其值,这就是为什么会发生这种情况:
my Int:D $foo = 1;
BEGIN say $foo; # OUTPUT: (Int)
constant
并非如此;编译器在编译过程中设置符号的值。这意味着对于问题中的示例,我们可以动态生成一个供handles
使用的方法列表,如下所示:
my Array[Str:D] constant PARENT_METHOD_OVERRIDES .= new: <name>;
my Array[Str:D] constant PARENT_METHODS .= new:
((), Metamodel::Naming)
.reduce({ (|$^methods, |$^role.HOW.methods: $^role) })
.map(*.name)
.grep(PARENT_METHOD_OVERRIDES ∌ *);
has Mu $!parent;
has Mu $!parent_meta handles PARENT_METHODS;
如果由于某种原因在类型的包中没有PARENT_METHOD_OVERRIDES
和PARENT_METHODS
之类的符号,则仍可以通过声明常量并在闭包内添加属性来以这种方式处理特征。方法和属性声明的默认范围是our
,因此您可以在类型包中的任何位置编写它们,并且仍然可以将它们添加到类型中。请记住,方法和属性是在编译期间处理的,因此,这不是处理诸如为类型动态生成属性或方法之类的方法。