如何使用元对象协议向对象添加属性?

Håk*_*and 9 class perl6 raku

我试图回答这个问题,并认为我可以使用元对象协议向类添加属性.这是一个最小的例子,我尝试在构造后test向类中添加一个属性Configuration:

use v6;

class Configuration {
}

my $config = Configuration.new;
my $attr = Attribute.new(
    :name('$.test'), # Trying to add a "test" attribute
    :type(Str),
    :has_accessor(1), 
    :package(Configuration)
);
$config.^add_attribute( $attr );
$config.^compose();
say "Current attributes: ", join ', ', $config.^attributes();
$attr.set_value( $config, "Hello" ); # <-- This fails with no such attribute '$.test'
say $config.test;
Run Code Online (Sandbox Code Playgroud)

当我运行这个时,我得到:

Current attributes: $.test
P6opaque: no such attribute '$.test' on type Configuration in a Configuration when trying to bind a value
  in block <unit> at ./p.p6 line 16
Run Code Online (Sandbox Code Playgroud)

Jon*_*ton 9

在类组合时间之后不能添加属性,这在编译}程序时达到结束时的编译时发生.(这是P6opaque表示的情况.表示可能存在允许这样的表示并非不可能,但此时没有指定.)

除此之外,.^add_attribute在元对象上调用,对于class属性是每个类型,而不是每个对象; 代码结构表明,期望可能是每个对象.没有任何东西可以让原型面向对象变得不可能(实际上MOP的设计使得有人可以在Perl 6中实现这样的对象系统),但是Perl 6本身并没有指定提供这一点.

因此,利用所提供的对象系统,这种操作需要在编译时和关闭之前完成}.这可以通过以下方式实现:

class Configuration {
    BEGIN {
        my $attr = Attribute.new(
            :name('$!test'), # Trying to add a "test" attribute
            :type(Str),
            :has_accessor(1),
            :package(Configuration)
        );
        Configuration.^add_attribute( $attr );
    }
}

my $config = Configuration.new;
say "Current attributes: ", join ', ', $config.^attributes();
$config.^attributes[0].set_value( $config, "Hello" );
say $config.test;
Run Code Online (Sandbox Code Playgroud)

这是Perl 6动态的许多地方之一,主要是邀请程序员参与编译时,而不是在运行时使所有事情都成为可能.

最后,我将注意到有一种方法可以将属性添加到现有对象,并且基于每个对象:通过使用does将角色混合到其中.这可以通过改变对象的类型来实现.还有一些文档does 在这里.