特性で使用する値を動的に生成するにはどうすればよいですか?


8

私が書いているライブラリの場合、その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 ∌ *)
};

では、このような場合に使用する特性の値を動的にどのように生成しますか?

回答:


7

トレイトアプリケーションはコンパイル時に設定されるため、コンパイル時に使用するための値を生成する方法も必要です。これは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_OVERRIDESPARENT_METHODSタイプのコンテキスト内に存在してはいけません、あなたはまだ、定数を宣言し、クロージャ内から属性を追加することにより、特性をこのように扱うことができます。メソッドと属性の宣言は、型のパッケージ内のどこからでも記述できるようにスコープが設定されていて、それらを型に追加することができます。メソッドと属性はコンパイル時に処理されるため、これは、型の属性またはメソッドを動的に生成するような方法では処理しないことに注意してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.