Raku rebless 和多个类 [英] Raku rebless and multiple classes

查看:53
本文介绍了Raku rebless 和多个类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(这是对以下内容的跟进:Raku rebless 不再适用于继承类)

(This is a follow up to: Raku rebless doesn't work with inherited classes anymore)

我试图想出一个更复杂的用例,但我无法让代码工作.

I have tried to come up with a more complex use case, but am unable to get the code to work.

这个想法是一个 Person 类,具有用于 Child 和 Adult 的 mixin 子类.我们有一个 Child 对象,当年龄超过 18 岁时将类型更改为 Adult.

The idea is a Person class, with mixin subclasses for Child and Adult. We have a Child object, and change the type to Adult when the age passes 18 year.

这个显然失败了,因为 Adult 是 Parent 上的 mixin,而不是 Child 上的:

This one obviously fails, as Adult is a mixin on Parent, and not on 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

只用一个类和一个 mixin 就可以设置它:

Setting it up with just one class and one mixin is the thing:

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.所以我试着存根类:

I get that. The rebless line refers to Adult, which hasn't been declared yet. So I tried stubbing the 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;
}

但是存根和继承不喜欢彼此:

But stubbing and inheritance doesn't like each other:

===SORRY!=== Error while compiling vote-error2
'Child+{<anon|1>}' cannot inherit from 'Child' because 'Child' isn't composed yet (maybe it is stubbed)

然后我尝试添加一个新的 mixin 来避免循环引用问题:

Then I tried adding a new mixin to avoid the circular reference problem:

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 不是我们现在拥有的 mixin.但错误消息不是很有帮助.

And it did as $tom is now something else than a Child, and Adult isn't a mixin of what we now have. But the error message isn't very helpful.

最后一个和第一个基本一样.

The last one is essentially the same as the first one.

我被卡住了.

推荐答案

TL;DR 我描述了几个问题.我在最后展示了一个在最近的(2020 年)Rakudo 上编译和运行的解决方案.这是您自己代码的一个简单变体,但我的知识不足,无法保证其正确性,更不用说适当性[1] [2].

TL;DR I describe several issues. I show a solution at the end that compiles and runs on a recent (2020) Rakudo. It's a simple variant of your own code but I am not knowledgeable enough to vouch for its correctness let alone appropriateness[1] [2].

无法改变 Any 类型对象的类型

错误信息来自 rebless 行:

Metamodel::Primitives.rebless($, Adult) if $.age == 18;

一个 $ 作为一个术语[3] 并不意味着 self 而是一个 匿名状态Scalar 变量.默认情况下,它包含一个 Any,因此是错误消息.应该是self.[4]

A $ as a term[3] does not mean self but instead an anonymous state Scalar variable. By default it contains an Any, hence the error message. It should be self.[4]

解决了第一个问题后,根据使用的 Rakudo 版本,我们得到了一个新问题:

Having fixed this first problem, we get a new one depending on which Rakudo version is used:

  • Older Rakudo:P6opaque 中不兼容的 MRO 为儿童和成人类型提供支持.

较新的乐堂:New type Adult for Child 不是 mixin 类型.

和我们刚刚修复的第一条错误信息一样,这两条也是由rebless语句触发的.[5]

Like the first error message we just fixed, these two are also triggered by the rebless statement.[5]

我们必须解决这两个问题.

在较新的 Rakudo 中,修复了 无法更改 Any 类型对象的类型 not a mixin type 问题不是如果我使用你的添加一个新的混合"代码就足够了;我刚刚收到 Incompatible MROs 错误.

In a newer Rakudo, fixing the Cannot change the type of a Any type object and the not a mixin type problems aren't enough if I use your "adding a new mixin" code; I just get the Incompatible MROs error.

相反,使用替代代码来修复旧 Rakudo 上的 Incompatible MROs 问题会导致 not a mixin type 除非 那个 问题是妥善解决.(在这个答案的原始版本中,我解决了 Incompatible MROs 问题——然后忽略了在较新的 Rakudo 上进行测试!)

Conversely, using alternate code that fixes the Incompatible MROs problem on an older Rakudo leads to the not a mixin type unless that problem is properly addressed. (In my original version of this answer I solved the Incompatible MROs problem -- and then neglected to test on a newer Rakudo!)

您对 Incompatible MROs 错误的诊断是这个显然失败了,因为 AdultPerson 上的 mixin,而不是 Person代码>子".我读了,看了一眼代码,相信了你,然后继续前进.但是后来我使用您为尝试解决它而编写的代码又回到了同样的问题.什么给?

Your diagnosis of the Incompatible MROs error was "This one obviously fails, as Adult is a mixin on Person, and not on Child". I read that, glanced at the code, believed you, and moved on. But then I'd arrived back at the same problem using code you'd written to try address it. What gives?

根据我的实验,似乎不仅to"类(其类是被重新祝福的对象的新类)必须有一个与被重新祝福的对象兼容的 MRO,根据我的期望(如类继承)但来自"对象(被祝福的对象)不能两者:

Based on my experiments, it seems that not only must the "to" class (whose class is to be the new class of the object being reblessed) have an MRO that's compatible with the object being reblessed according to things I would expect (like class inheritance) but also the "from" object (the one being reblessed) cannot be both:

  • 基于具有属性的类.

  • Based on a class that has attributes.

已经混进去了.

(我不知道这是一个可以修复的错误还是一个不可避免的限制.我知道最近(2020 年)Rakudo 使用之前 SO 中提供的代码 Jonathan 的两种变体都有这个限制.)

(I don't know if this is a bug that can be fixed or an unavoidable restriction. I do know a recent (2020) Rakudo has this constraint using both variations of the code Jonathan provided in the previous SO.)

这意味着添加新的 mixin 以避免循环引用问题"(存根和继承不相容")并不能解决您的问题.

This means that "adding a new mixin to avoid the circular reference problem" ("stubbing and inheritance doesn't like each other") doesn't solve your problem.

相反,我回到了你的只有一个类和一个 mixin"的尝试(结果是你最初编写的形式中的 Illegally post-declared type)并尝试了另一种方法来获得围绕那个错误.

Instead, I went back to your "just one class and one mixin" attempt (which ended up with Illegally post-declared type in the form you originally wrote it) and tried another approach to get around that error.

只有一个类和一个混合"代码的以下变体适用于 Rakudo v2020.01.114.gcfe.2.cdc.56.我所做的只是将 Adult 常量转换为变量.我已经为其余与您的代码相同的代码编写了 ...:

The following variant of your "just one class and one mixin" code works on a Rakudo v2020.01.114.gcfe.2.cdc.56. All I've done is turned the Adult constant into a variable. I've written ... for the rest of the code which is the same as your code:

my $Adult;

...
    Metamodel::Primitives.rebless(self, $Adult) if $.age == 18;
...

$Adult = Child but role { method can-vote { True } }
$Adult.^set_name('Adult');

...

嗯.

[1] Jonathan 在最近的 SO 中的解决方案使用 compile-time 构造用于 Adult.我的解决方案遵循 Jonathan 的示例,不同之处在于它在 运行时 构造了 rebless 目标 $Adult.面对@JonathanWorthington 引入的新优化,我不确定这在技术上是否安全.我会试着召唤"他评论.

[1] Jonathan's solution in a recent SO used compile-time constructs for Adult. My solution follows Jonathan's example except that it constructs the rebless target $Adult at run-time. I'm unsure if this is technically safe in the face of the new optimization @JonathanWorthington has introduced. I will try to "summon" him to comment on it.

[2] 除了这个脚注之外,我的回答没有解决使用 rebless智慧.我立刻想到了两个问题.首先是可靠的功能给定turophilia,这显然对您至关重要,甚至需要询问您最近的 SO.(有了它,metaaturophilia.也就是说,我们目前在使 Raku(语言)和 Rakudo(实现)成熟的方法上存在漏洞.我们中的一个人编写的代码导致漏洞被填补,我们都可以感激.) 其次是 MOP 的可靠文档,因为(据我所知)一些关键文档打破了根据烤将自身约束到 Raku 规范的一般规则,而是 "很大程度上反映了 Rakudo Raku 编译器实现的元对象系统".我只是解决错误,直到您的代码在 2020 版 Rakudo 上编译和运行时没有错误.

[2] Other than this footnote, my answer does not address the wisdom of using rebless. Two issues come immediately to mind for me. First is reliable functionality given 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". I just address errors until your code compiles and runs without error on a 2020 version of Rakudo.

[3] 请参阅什么是术语?此评论.

[4] 有些人可能会认为如果 $.fooself.foo,那么$必须self.如果 raku 具有用于大多数编程语言的典型的上下文无关标记化,那么这种想法将是一个合理的假设.此外,它通常也适用于 Raku 代码,就像它通常适用于自然语言一样.(如果英文标记my"后面跟着self",那么它很可能与myself"意思相同.)但 Raku 的语法故意结合了 上下文敏感性无扫描仪解析最大咀嚼 以支持创建比典型的编程语言更自然的感觉语言.在这里我们看到一个例子.在术语位置"[3] 中,输入 $.foo 被识别为单个标记而不是两个标记($ 后跟 .foo) 而输入 $,... 被识别为两个标记($ 后跟列表分隔符 ,) 而不是一个.

[4] Some folk might presume that if $.foo is a .foo of self, then $ must be self. Such thinking would be a reasonable presumption if raku had the typical context-free tokenization used for most programming languages. Moreover, it generally applies to Raku code too, just as it generally applies even in natural language. (If the English token "my" is followed by "self", then it likely means the same as "myself".) But Raku's grammar deliberately combines context sensitivity, scannerless parsing and maximal munch in order to support creation of more natural feeling languages than is typical for programming languages. And here we see an example. In "term position"[3] the input $.foo is recognized as a single token instead of two ($ followed by .foo) whereas the input $,... is recognized as two tokens ($ followed by the list separator operator ,) rather than one.

[5] 所有这些错误信息都是在乐堂靠近金属的部分产生的.如果你使用 MoarVM 作为你的后端,它们来自它的 P6opaque.c 文件.

[5] All of these error messages are generated in parts of Rakudo that are close to the metal. If you're using MoarVM as your backend they come from its P6opaque.c file.

这篇关于Raku rebless 和多个类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆