作者:帅呆了2014_329 | 来源:互联网 | 2022-12-05 14:10
我正在继续探索Perl6深层次的细微实现细节.这次我将自己的方法安装到角色中时遇到问题.当我们开始进入代码之旅时,请系好安全带.
这个想法是一个属性特征,它将方法安装在它所组成的类型对象上.这个问题最初是在私有方法上发现的,我希望将其安装在声明属性的角色中.此时我发现在某些条件下生成的方法引用了来自其闭包的标量无法调用!很可能是由于关闭在运行时丢失.但最令人困惑的一点是,它只发生在角色上,只有当一个角色正在消耗另一个角色时!
所以,这是特质来源:
unit module trait-foo;
role FooClassHOW {...}
role FooAttr {
has $.base-name = self.name.substr(2);
method compose (Mu \type) {
callsame;
if (type.HOW ~~ Metamodel::ClassHOW) && (type.HOW !~~ FooClassHOW) {
type.HOW does FooClassHOW;
}
}
method install-method ( Mu \type ) {
my $attr = self;
type.^add_private_method(
"attr-{$attr.base-name}",
method { "by attr {$attr.name}" }
);
type.^add_method(
"pubattr-{$attr.base-name}",
method { "by attr {$attr.name} - public" }
);
type.^add_private_method(
"check-{$attr.base-name}",
method { "not using closure" }
);
}
}
role FooClassHOW {
method compose ( Mu \type ) {
for type.^attributes.grep( FooAttr ) -> $attr {
$attr.install-method( type );
type.^add_private_method(
"class-{$attr.base-name}",
method { "by class: attr {$attr.name}" }
);
}
nextsame;
}
}
role FooRoleHOW {
method compose ( Mu \type ) {
for type.^attributes.grep( FooAttr ) -> $attr {
$attr.install-method( type );
type.^add_private_method(
"role-{$attr.base-name}",
method { "by role: attr {$attr.name}" }
);
}
nextsame;
}
}
multi trait_mod: (Attribute:D $attr, :$foo!) is export {
$attr does FooAttr;
given $*PACKAGE.HOW {
when Metamodel::ParametricRoleHOW {
$_ does FooRoleHOW unless $_ ~~ FooRoleHOW;
}
default {
$_ does FooClassHOW unless $_ ~~ FooClassHOW;
}
}
}
这里的关键点是install-method
它安装一个公共方法pubattr-
和私有方法attr-
,check-
.之间的差pubattr-
,attr-
并且check-
是当后者没有前两个都是指它们的闭合.如果在各个文件中定义了两个角色和一个类,会发生以下情况:
compose_method_inject.p6
#!/usr/bin/env perl6
use lib '.';
use trait-foo;
use compose-foorole;
class Foo does FooRole {
has $.fubar is foo;
method class-test {
say self!check-fubar;
say self!class-fubar;
say self!attr-fubar;
}
}
my $inst = Foo.new;
note "> Class";
$inst.class-test;
note "> BarRole";
$inst.bar-role-test;
note "> FooRole";
$inst.foo-role-test;
撰写-foorole.pm6
unit package compose;
use trait-foo;
use compose-barrole;
role FooRole does BarRole is export {
has $.foo is foo;
method foo-role-test {
note FooRole.^candidates[0].^private_method_table;
say self!check-foo;
say self!role-foo;
say self!attr-foo;
}
}
撰写-barrole.pm6
unit package compose;
use trait-foo;
role BarRole is export {
has $.bar is foo;
method bar-role-test {
note BarRole.^candidates[0].^private_method_table;
say self!check-bar;
say self!role-bar;
say self!inattr-bar;
}
}
执行compose_method_inject.p6会产生以下输出:
> Class
not using closure
by class: attr $!fubar
by attr $!fubar
by attr $!fubar - public
> BarRole
{attr-bar => , check-bar => , role-bar => }
not using closure
by role: attr $!bar
Cannot invoke this object (REPR: Null; VMNull)
请注意,该类工作正常,而类似的代码BarRole
失败.如果首先执行foo-role-test
from,FooRole
则会观察到相同的结果:
> Class
not using closure
by class: attr $!fubar
by attr $!fubar
by attr $!fubar - public
> FooRole
{attr-foo => , check-foo => , role-foo => }
not using closure
by role: attr $!foo
Cannot invoke this object (REPR: Null; VMNull)
还值得注意的是,安装的方法FooRoleHOW
不会失去其关闭并且已成功执行.
现在,另一招.我删除does BarRole
从FooRole
,使之直接应用于富:
class Foo does FooRole does BarRole {
输出变化很大,情况变得更加混乱:
> Class
not using closure
by class: attr $!fubar
by attr $!fubar
by attr $!fubar - public
> FooRole
{attr-foo => , check-foo => , role-foo => }
not using closure
by role: attr $!foo
by attr $!foo
> BarRole
{attr-bar => , check-bar => , role-bar => }
not using closure
by role: attr $!bar
Cannot invoke this object (REPR: Null; VMNull)
UPD另一个需要注意的重要事项是,角色和类有意地按文件分割,因为将它们全部放在公共文件中会使事情按预期工作.
顺便说一句,我不想深入了解它,但在原始代码中,上面提取的样本我也设置了方法名称.set_name
.名称是字符串,包括$attr
闭包的标量.compose()
正在生成哈希的转储方法表,其中集合名称为值; 在用户代码中转储相同的表显示类似于上面的输出 - 使用
as值.看起来,方法名称与封闭一起进行了GC.
现在,我想听一些人说我很蠢,方法必须以不同的方式安装.或者有关属性的信息必须以其他方式保留,而不是依赖于闭包.或任何其他想法,让我创建私有属性相关的方法.