Raku rebless 和多个 类
Raku rebless and multiple classes
(这是跟进:)
我试图想出一个更复杂的用例,但我无法让代码工作。
这个想法是一个人 class,带有针对儿童和成人的混合子classes。我们有一个 Child 对象,当年龄超过 18 岁时,将类型更改为 Adult。
这个显然失败了,因为 Adult 是 Parent 的 mixin,而不是 Child:
class Person
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
# Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
...;
}
}
constant Adult = Person but role { method can-vote { True } }
constant Child = Person but role
{
method can-vote { False }
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless(self, Adult) if $.age == 18;
}
}
BEGIN Child.^set_name('Child');
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但它部分运行:
Age Can-Vote Class
0 False Child
1 False Child
2 False Child
3 False Child
4 False Child
5 False Child
6 False Child
7 False Child
8 False Child
9 False Child
10 False Child
11 False Child
12 False Child
13 False Child
14 False Child
15 False Child
16 False Child
17 False Child
Incompatible MROs in P6opaque rebless for types Child and Adult
in method happy-birthday at ./vote-error line 28
只用一个 class 和一个 mixin 就可以了:
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
constant Adult = Child but role { method can-vote { True } }
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
除了它不起作用:
Error while compiling vote-error1
Illegally post-declared type:
Adult used at line 10
我明白了。 rebless 行指的是尚未声明的 Adult。所以我试着存根 class:
class Child { ... }
constant Adult = Child but role { method can-vote { True } }
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但是存根和继承互不相容:
===SORRY!=== Error while compiling vote-error2
'Child+{<anon|1>}' cannot inherit from 'Child' because 'Child' isn't composed yet (maybe it is stubbed)
然后我尝试添加一个新的 mixin 来避免循环引用问题:
class Child
{
has Int $.age is rw = 0;
method can-vote
{
False;
}
}
constant Adult = Child but role { method can-vote { True } }
BEGIN Adult.^set_name('Adult');
role still-a-child
{
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
}
my $tom = Child.new but still-a-child;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但这也失败了:
Age Can-Vote Class
0 False Child+{still-a-child}
1 False Child+{still-a-child}
2 False Child+{still-a-child}
3 False Child+{still-a-child}
4 False Child+{still-a-child}
5 False Child+{still-a-child}
6 False Child+{still-a-child}
7 False Child+{still-a-child}
8 False Child+{still-a-child}
9 False Child+{still-a-child}
10 False Child+{still-a-child}
11 False Child+{still-a-child}
12 False Child+{still-a-child}
13 False Child+{still-a-child}
14 False Child+{still-a-child}
15 False Child+{still-a-child}
16 False Child+{still-a-child}
17 False Child+{still-a-child}
Cannot change the type of a Any type object
in method happy-birthday at vote-error3 line 26
它确实如此,因为 $tom 现在不是 Child,而 Adult 不是我们现在拥有的东西的混合体。但是错误消息不是很有用。
最后一个和第一个基本一样
我卡住了。
TL;DR 我描述了几个问题。我在最后展示了一个在最近(2020 年)Rakudo 上编译和 运行s 的解决方案。这是您自己代码的一个简单变体,但我的知识不足以保证其正确性,更不用说适当性了[1] [2].
Cannot change the type of a Any type object
错误消息来自 rebless
行:
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
$
作为术语[3]并不意味着self
而是anonymous state Scalar
variable。默认情况下,它包含一个 Any
,因此会出现错误消息。应该是self
.[4]
解决了第一个问题后,根据使用的 Rakudo 版本,我们得到了一个新问题:
老乐道:Incompatible MROs in P6opaque rebless for types Child and Adult
.
较新的乐道:New type Adult for Child is not a mixin type
.
就像我们刚刚修复的第一个错误消息一样,这两个也是由 rebless
语句触发的。[5]
我们必须解决两个个问题。
在较新的 Rakudo 中,修复 Cannot change the type of a Any type object
和 not a mixin type
问题是不够的,如果我使用你的 "adding a new mixin" 代码;我刚收到 Incompatible MROs
错误。
相反,使用替代代码修复旧 Rakudo 上的 Incompatible MROs
问题会导致 not a mixin type
,除非 问题得到妥善解决。 (在我这个答案的原始版本中,我解决了 Incompatible MROs
问题——然后忽略了在更新的 Rakudo 上进行测试!)
您对 Incompatible MROs
错误的诊断是 "This one obviously fails, as Adult
is a mixin on Person
, and not on Child
"。我读了,看了一眼代码,相信你,然后继续前进。但后来我又遇到了同样的问题,使用你编写的代码来尝试解决它。给出了什么?
根据我的实验,似乎不仅"to" class(其class是被祝福对象的新class)有一个 MRO 与根据我期望的事情被重新祝福的对象兼容(比如 class 继承)但是 还有 "from" 对象(被重新祝福的人)不能 两者都:
基于具有属性的 class。
已经混入。
(我不知道这是可以修复的错误还是不可避免的限制。我知道最近(2020 年)Rakudo 使用 Jonathan 在之前的 SO 中提供的两种代码变体都有此限制。 )
这意味着 "adding a new mixin to avoid the circular reference problem" ("stubbing and inheritance doesn't like each other") 不能解决您的问题。
相反,我回到了您的 "just one class and one mixin" 尝试(以您最初编写的形式以 Illegally post-declared type
结束)并尝试了另一种方法来绕过 错误。
您的 "just one class and one mixin" 代码的以下变体适用于 Rakudo v2020.01.114.gcfe.2.cdc.56。我所做的只是将 Adult
常量转换为变量。我已经为其余代码编写了 ...
,这与您的代码相同:
my $Adult;
...
Metamodel::Primitives.rebless(self, $Adult) if $.age == 18;
...
$Adult = Child but role { method can-vote { True } }
$Adult.^set_name('Adult');
...
Hth.
脚注
[1] Jonathan 在最近的 SO 中的解决方案使用 compile-time 构造 Adult
。我的解决方案遵循 Jonathan 的示例,只是它在 运行-time 处构造了 rebless 目标 $Adult
。面对@JonathanWorthington 引入的新优化,我不确定这在技术上是否安全。我会尽量"summon"他评论一下。
[2] 除此脚注外,我的回答并未涉及使用 rebless
的 智慧 。我立即想到两个问题。首先是给定 turophilia, which is clearly central to you even needing to ask your recent SOs. (And with it, metaturophilia. That is, we currently have holes in our approach to maturing Raku, the language, and Rakudo, the implementation. To the degree code written by one of us leads to holes being filled in we can all be grateful.) Second is reliable documentation of the MOP given that (as far as I can tell) some key documentation breaks the general rule of constraining itself to the Raku specification according to roast and instead "largely reflects the metaobject system as implemented by the Rakudo Raku compiler" 的可靠 功能 。我只是解决错误,直到您的代码编译成功并且 运行s 在 2020 版 Rakudo 上没有错误。
[3] 请参阅 什么是术语? 与 this comment.[=55 中的某些上下文相关联=]
[4] 有些人可能会认为如果 $.foo
是 self
的 .foo
,那么 $
必须是self
。如果 raku 具有用于大多数编程语言的典型的上下文无关标记化,那么这种想法将是一个合理的假设。此外,它 通常 也适用于 Raku 代码,就像它通常适用于自然语言一样。 (如果英文标记 "my" 后跟 "self",那么它可能与 "myself" 意思相同。)但 Raku 的语法故意结合 context sensitivity, scannerless parsing and maximal munch 以支持创建更多自然感觉语言比典型的编程语言。在这里我们看到一个例子。在 "term position"[3] 中,输入 $.foo
被识别为单个标记而不是两个($
后跟 .foo
),而输入 $,...
被识别为两个标记($
后跟列表分隔符运算符 ,
)而不是一个。
[5] 所有这些错误信息都是在靠近金属的Rakudo部分产生的。如果您使用 MoarVM 作为后端,它们来自其 P6opaque.c 文件。
谢谢。但是我无法让它工作:
class Adult { ... }
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless(self, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
role grown-up { method can-vote { True } }
class Adult is Child does grown-up { }
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
这运行到 17,然后失败
New type Adult for Child is not a mixin type
我错过了什么?
(Stackowerflow 应该允许在注释中添加代码。)
(这是跟进:
我试图想出一个更复杂的用例,但我无法让代码工作。
这个想法是一个人 class,带有针对儿童和成人的混合子classes。我们有一个 Child 对象,当年龄超过 18 岁时,将类型更改为 Adult。
这个显然失败了,因为 Adult 是 Parent 的 mixin,而不是 Child:
class Person
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
# Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
...;
}
}
constant Adult = Person but role { method can-vote { True } }
constant Child = Person but role
{
method can-vote { False }
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless(self, Adult) if $.age == 18;
}
}
BEGIN Child.^set_name('Child');
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但它部分运行:
Age Can-Vote Class
0 False Child
1 False Child
2 False Child
3 False Child
4 False Child
5 False Child
6 False Child
7 False Child
8 False Child
9 False Child
10 False Child
11 False Child
12 False Child
13 False Child
14 False Child
15 False Child
16 False Child
17 False Child
Incompatible MROs in P6opaque rebless for types Child and Adult
in method happy-birthday at ./vote-error line 28
只用一个 class 和一个 mixin 就可以了:
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
constant Adult = Child but role { method can-vote { True } }
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
除了它不起作用:
Error while compiling vote-error1
Illegally post-declared type:
Adult used at line 10
我明白了。 rebless 行指的是尚未声明的 Adult。所以我试着存根 class:
class Child { ... }
constant Adult = Child but role { method can-vote { True } }
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
BEGIN Adult.^set_name('Adult');
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但是存根和继承互不相容:
===SORRY!=== Error while compiling vote-error2
'Child+{<anon|1>}' cannot inherit from 'Child' because 'Child' isn't composed yet (maybe it is stubbed)
然后我尝试添加一个新的 mixin 来避免循环引用问题:
class Child
{
has Int $.age is rw = 0;
method can-vote
{
False;
}
}
constant Adult = Child but role { method can-vote { True } }
BEGIN Adult.^set_name('Adult');
role still-a-child
{
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
}
}
my $tom = Child.new but still-a-child;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
但这也失败了:
Age Can-Vote Class
0 False Child+{still-a-child}
1 False Child+{still-a-child}
2 False Child+{still-a-child}
3 False Child+{still-a-child}
4 False Child+{still-a-child}
5 False Child+{still-a-child}
6 False Child+{still-a-child}
7 False Child+{still-a-child}
8 False Child+{still-a-child}
9 False Child+{still-a-child}
10 False Child+{still-a-child}
11 False Child+{still-a-child}
12 False Child+{still-a-child}
13 False Child+{still-a-child}
14 False Child+{still-a-child}
15 False Child+{still-a-child}
16 False Child+{still-a-child}
17 False Child+{still-a-child}
Cannot change the type of a Any type object
in method happy-birthday at vote-error3 line 26
它确实如此,因为 $tom 现在不是 Child,而 Adult 不是我们现在拥有的东西的混合体。但是错误消息不是很有用。
最后一个和第一个基本一样
我卡住了。
TL;DR 我描述了几个问题。我在最后展示了一个在最近(2020 年)Rakudo 上编译和 运行s 的解决方案。这是您自己代码的一个简单变体,但我的知识不足以保证其正确性,更不用说适当性了[1] [2].
Cannot change the type of a Any type object
错误消息来自 rebless
行:
Metamodel::Primitives.rebless($, Adult) if $.age == 18;
$
作为术语[3]并不意味着self
而是anonymous state Scalar
variable。默认情况下,它包含一个 Any
,因此会出现错误消息。应该是self
.[4]
解决了第一个问题后,根据使用的 Rakudo 版本,我们得到了一个新问题:
老乐道:
Incompatible MROs in P6opaque rebless for types Child and Adult
.较新的乐道:
New type Adult for Child is not a mixin type
.
就像我们刚刚修复的第一个错误消息一样,这两个也是由 rebless
语句触发的。[5]
我们必须解决两个个问题。
在较新的 Rakudo 中,修复 Cannot change the type of a Any type object
和 not a mixin type
问题是不够的,如果我使用你的 "adding a new mixin" 代码;我刚收到 Incompatible MROs
错误。
相反,使用替代代码修复旧 Rakudo 上的 Incompatible MROs
问题会导致 not a mixin type
,除非 问题得到妥善解决。 (在我这个答案的原始版本中,我解决了 Incompatible MROs
问题——然后忽略了在更新的 Rakudo 上进行测试!)
您对 Incompatible MROs
错误的诊断是 "This one obviously fails, as Adult
is a mixin on Person
, and not on Child
"。我读了,看了一眼代码,相信你,然后继续前进。但后来我又遇到了同样的问题,使用你编写的代码来尝试解决它。给出了什么?
根据我的实验,似乎不仅"to" class(其class是被祝福对象的新class)有一个 MRO 与根据我期望的事情被重新祝福的对象兼容(比如 class 继承)但是 还有 "from" 对象(被重新祝福的人)不能 两者都:
基于具有属性的 class。
已经混入。
(我不知道这是可以修复的错误还是不可避免的限制。我知道最近(2020 年)Rakudo 使用 Jonathan 在之前的 SO 中提供的两种代码变体都有此限制。 )
这意味着 "adding a new mixin to avoid the circular reference problem" ("stubbing and inheritance doesn't like each other") 不能解决您的问题。
相反,我回到了您的 "just one class and one mixin" 尝试(以您最初编写的形式以 Illegally post-declared type
结束)并尝试了另一种方法来绕过 错误。
您的 "just one class and one mixin" 代码的以下变体适用于 Rakudo v2020.01.114.gcfe.2.cdc.56。我所做的只是将 Adult
常量转换为变量。我已经为其余代码编写了 ...
,这与您的代码相同:
my $Adult;
...
Metamodel::Primitives.rebless(self, $Adult) if $.age == 18;
...
$Adult = Child but role { method can-vote { True } }
$Adult.^set_name('Adult');
...
Hth.
脚注
[1] Jonathan 在最近的 SO 中的解决方案使用 compile-time 构造 Adult
。我的解决方案遵循 Jonathan 的示例,只是它在 运行-time 处构造了 rebless 目标 $Adult
。面对@JonathanWorthington 引入的新优化,我不确定这在技术上是否安全。我会尽量"summon"他评论一下。
[2] 除此脚注外,我的回答并未涉及使用 rebless
的 智慧 。我立即想到两个问题。首先是给定 turophilia, which is clearly central to you even needing to ask your recent SOs. (And with it, metaturophilia. That is, we currently have holes in our approach to maturing Raku, the language, and Rakudo, the implementation. To the degree code written by one of us leads to holes being filled in we can all be grateful.) Second is reliable documentation of the MOP given that (as far as I can tell) some key documentation breaks the general rule of constraining itself to the Raku specification according to roast and instead "largely reflects the metaobject system as implemented by the Rakudo Raku compiler" 的可靠 功能 。我只是解决错误,直到您的代码编译成功并且 运行s 在 2020 版 Rakudo 上没有错误。
[3] 请参阅 什么是术语? 与 this comment.[=55 中的某些上下文相关联=]
[4] 有些人可能会认为如果 $.foo
是 self
的 .foo
,那么 $
必须是self
。如果 raku 具有用于大多数编程语言的典型的上下文无关标记化,那么这种想法将是一个合理的假设。此外,它 通常 也适用于 Raku 代码,就像它通常适用于自然语言一样。 (如果英文标记 "my" 后跟 "self",那么它可能与 "myself" 意思相同。)但 Raku 的语法故意结合 context sensitivity, scannerless parsing and maximal munch 以支持创建更多自然感觉语言比典型的编程语言。在这里我们看到一个例子。在 "term position"[3] 中,输入 $.foo
被识别为单个标记而不是两个($
后跟 .foo
),而输入 $,...
被识别为两个标记($
后跟列表分隔符运算符 ,
)而不是一个。
[5] 所有这些错误信息都是在靠近金属的Rakudo部分产生的。如果您使用 MoarVM 作为后端,它们来自其 P6opaque.c 文件。
谢谢。但是我无法让它工作:
class Adult { ... }
class Child
{
has Int $.age is rw = 0;
method happy-birthday
{
$.age++;
Metamodel::Primitives.rebless(self, Adult) if $.age == 18;
}
method can-vote
{
False;
}
}
role grown-up { method can-vote { True } }
class Adult is Child does grown-up { }
my $tom = Child.new;
say "Age Can-Vote Class";
for ^20
{
say "{ $tom.age.fmt('%3d') } { $tom.can-vote } { $tom.^name }";
$tom.happy-birthday;
}
这运行到 17,然后失败
New type Adult for Child is not a mixin type
我错过了什么?
(Stackowerflow 应该允许在注释中添加代码。)