Wie können Sie dynamisch Werte für die Verwendung mit Merkmalen generieren?

8

Für eine Bibliothek, die ich schreibe, habe ich ein Attribut für ein WIE, das das handlesMerkmal verwendet, um Methoden verschiedener Rollen, die von einem anderen WIE ausgeführt werden, an eine Instanz dieses WIE zu delegieren. Mein erster Versuch sah so aus (obwohl dies nur einfacher zu lesen ist, wird dies nur behandelt 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

Das Problem dabei ist, dass wenn einer der Typen, die ich in diesem HOW-Verfahren verwende, Methoden für immer ändert, dies nicht mehr mit allen Methoden funktioniert. Stattdessen möchte ich dynamisch eine Liste von Methoden generieren, die mit einer Liste von Methoden behandelt werden sollen, die in diesem HOW überschrieben werden, und eine Liste von Typen, deren Methoden behandelt werden sollen. Dies ist nicht so einfach, wie es sich anhört, da das handlesMerkmal implementiert ist. Zum Beispiel wird dies nicht funktionieren:

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 ∌ *)
};

Wie würden Sie also dynamisch einen Wert für ein Merkmal generieren, der in solchen Fällen verwendet werden kann?

Kaiepi
quelle

Antworten:

7

Die Trait-Anwendung wird während der Kompilierung eingerichtet, daher benötigen wir eine Möglichkeit, einen Wert zu generieren, der auch während der Kompilierung verwendet werden kann. Dies kann mit dem erfolgenBEGIN Phaser erfolgen, constantin diesem Fall ist dies jedoch besser beschrieben .

Konstanten in Raku sind nicht nur Variablen, die Sie nach dem Deklarieren nicht zuweisen oder binden können. Wenn Sie eine Variable deklarieren, wird ihr Symbol normalerweise während der Kompilierung installiert, aber ihr Wert wird erst zur Laufzeit festgelegt. Deshalb kann dies passieren:

my Int:D $foo = 1;

BEGIN say $foo; # OUTPUT: (Int)

Dies ist bei nicht der Fall constant; Der Compiler legt den Wert des Symbols während der Kompilierung fest. Dies bedeutet, dass wir für das Beispiel in der Frage dynamisch eine Liste von Methoden generieren können handles, die wie folgt verwendet werden können:

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;

Wenn aus irgendeinem Grund Symbole wie PARENT_METHOD_OVERRIDESund PARENT_METHODSnicht im Kontext des Typs vorhanden sein sollten, können Sie Merkmale dennoch auf diese Weise behandeln, indem Sie die Konstanten deklarieren und die Attribute innerhalb eines Abschlusses hinzufügen. Methoden- und Attributdeklarationen sind so angeordnet, dass Sie sie von überall im Paket des Typs schreiben und dennoch dem Typ hinzufügen können. Denken Sie daran, dass Methoden und Attribute während der Kompilierung behandelt werden. Auf diese Weise würden Sie also nicht so etwas wie das dynamische Generieren von Attributen oder Methoden für einen Typ behandeln.

Kaiepi
quelle