Wie können Sie ein Modul für eine Rollengruppe schreiben?

8

Eine Funktion von Rollen sind Rollengruppen, mit denen Sie mehrere Rollen mit demselben Namen deklarieren können, die unterschiedliche Parameter akzeptieren, ähnlich wie bei mehreren Routinen:

role Foo[Int:D] {
    method is-int(::?CLASS:_: --> True)  { }
    method is-str(::?CLASS:_: --> False) { }
}
role Foo[Str:D] {
    method is-int(::?CLASS:_: --> False) { }
    method is-str(::?CLASS:_: --> True)  { }
}

Oft haben Sie für einen Typ ein Modul. Das Problem ist, dass Sie nur eine unitDeklaration mit Gültigkeitsbereich in einem Modul haben können, sodass Sie sie nicht mit Rollengruppen verwenden können. Wie können Sie ein Modul für eine Rollengruppe schreiben?

Kaiepi
quelle

Antworten:

9

Sie können Module ohne unitGültigkeitsbereichsdeklarationen enthalten und Symbole daraus exportieren. Das Exportieren einer Rollengruppe ist jedoch ein Problem. Sie können das is exportMerkmal hierfür nicht verwenden , da dadurch die falschen Typen exportiert werden. Wenn Sie auf eine Rolle verweisen, nachdem sie deklariert wurde, beziehen Sie sich auf die Rollengruppe, nicht auf die einzelnen Rollen darin. Wenn Sie jedoch is exporteinzelne Rollen verwenden, werden diese einzelnen Rollen exportiert, nicht die Rollengruppe. Einzelne Rollen haben ein ganz anderes WIE als Rollengruppen und verhalten sich nicht so, wie Sie es normalerweise von Rollen erwarten würden!

Glücklicherweise gibt es eine Möglichkeit, dies mit dem EXPORTPaket zu tun . Wenn Sie die Rollengruppe Fooin diesem Paket EXPORT::DEFAULT::Foodeklarieren , erhalten Sie einen Namen von , den Sie wahrscheinlich nicht möchten. Sie möchten ihn daher im MYBereich der Einheit deklarieren und EXPORT::DEFAULTstattdessen eine Konstante dafür deklarieren :

use v6.d;

my role Foo[Int:D] {
    method is-int(::?CLASS:_: --> True)  { }
    method is-str(::?CLASS:_: --> False) { }
}
my role Foo[Str:D] {
    method is-int(::?CLASS:_: --> False) { }
    method is-str(::?CLASS:_: --> True)  { }
}

my package EXPORT::DEFAULT {
    constant Foo = ::Foo;
}

Jetzt Fookann importiert und verwendet werden OK:

use Foo;

say ::<Foo>:exists;       # OUTPUT: True
say try Foo[1].is-int;    # OUTPUT: True
say try Foo['ok'].is-str; # OUTPUT: True

Hinweis: Sie können keine ::konstanten Namen verwenden. Um eine Rollengruppe in einen Namespace zu exportieren, müssen Sie sie in ein anderes Paket einschließen:

my role Foo::Bar[Int:D] { }
my role Foo::Bar[Str:D] { }

my package EXPORT::DEFAULT {
    package Foo {
        constant Bar = Foo::Bar;
    }
}
Kaiepi
quelle
3

Verwenden Sie einfach den Standardbereich ourohne Umgebungsbedingungen unit.

unitnur wurde hinzugefügt , so dass Sie nicht mit einer ganzen Datei umgeben würden {und }wenn es nur einer module, package, class, role, oder subin der Datei.
Sie müssen es nicht immer verwenden.
In der Tat Sie nie haben , es zu benutzen.


Wenn Sie möchten, fügen Sie eine Vorwärtsdeklaration ohne Parametrierung hinzu.
Ein hinzugefügtes Merkmal gilt im Allgemeinen für alle Rollen mit demselben Namen.

lib/Foo/Bar.rakumod::

use v6.d;

role Foo::Bar {…} # is export would be added here

role Foo::Bar[Int:D] {
    method is-int(::?CLASS:_: --> True)  { }
    method is-str(::?CLASS:_: --> False) { }
}
role Foo::Bar[Str:D] {
    method is-int(::?CLASS:_: --> False) { }
    method is-str(::?CLASS:_: --> True)  { }
}

Wenn Sie es dann verwenden, wird es automatisch so geladen, dass es unter einem vollständig qualifizierten Namen zugänglich ist.

{
    use lib <lib>; # only needed because it is not installed

    use Foo::Bar;

    say Foo::Bar[ 1].is-int; # True
    say Foo::Bar[''].is-str; # True

    say Foo::Bar.^name; # Foo::Bar
}

say Foo::Bar.^name; # error: Could not find symbol 'Bar' in 'Foo'

In diesem Fall können Sie es in eine Modulanweisung einfügen, damit Sie nicht Foo::so oft schreiben müssen .

lib/Foo/Bar.rakumod::

use v6.d;

unit module Foo;

role Bar {…}

role Bar[Int:D] {
    method is-int(::?CLASS:_: --> True)  { }
    method is-str(::?CLASS:_: --> False) { }
}
role Bar[Str:D] {
    method is-int(::?CLASS:_: --> False) { }
    method is-str(::?CLASS:_: --> True)  { }
}

Die Rolle ist weiterhin zugänglich als Foo::Bar.
Es würde mich nicht wundern, wenn dies genau den gleichen Code wie im vorherigen Beispiel ergeben würde.

Der einzige Grund zum Hinzufügen is exportist, wenn Sie möchten, dass es als Barstatt exportiert wird Foo::Bar. Dies gilt für beide obigen Beispiele.


Ich vermute, dass Sie dachten, dass Sie immer verwenden mussten is export. In vielen Fällen wirklich nicht.

unit module Foo::Bar; # default `our` scoped

our sub baz ( --> 'hello world'){}
use Foo::Bar;

say Foo::Bar::baz(); # hello world
# this works because it was declared as `our`

Wenn Sie es nur verwenden baz()möchten, während es sich im Umfang des Moduls befindet, müssen Sie es dann und nur dann exportieren.

unit module Foo::Bar;

our sub baz ( --> 'hello world') is export {}
use Foo::Bar;
say Foo::Bar::baz(); # hello world

# available because of `is export`
say baz(); # hello world

Beachten Sie, dass ich es immer noch so deklariert habe our, dass jemand, der nicht möchte, dass Sie es exportieren, weiterhin für ihn zugänglich ist.

use Foo::Bar ();

# say baz(); # baz used at line 1. Did you mean 'bag'?

say Foo::Bar::baz(); # hello world

Der gesamte Zweck von is exportbesteht darin, die Notwendigkeit zu beseitigen, vollständig qualifizierte Namen für Funktionen zu verwenden. Dass es auch für Dinge wie Rollen funktioniert, ist ein Nebeneffekt.

Brad Gilbert
quelle
Ah, ich dachte, ourDeklarationen mit Gültigkeitsbereich in Modulen ohne unitDeklarationen mit Gültigkeitsbereich wurden aus irgendeinem Grund global. Ich erwähnte das Verhalten mit, is exportweil ich davon ausging, dass die Leute zuerst versuchen könnten, so etwas zu exportieren.
Kaiepi
Ich denke unit module Foosollte aber sein unit package Foo. Wenn es sich um ein Modul handelt und an Fooanderer Stelle vorhanden ist, werden die Symbole nicht ordnungsgemäß zusammengeführt, wenn beide Foo::Barund Fooimportiert werden.
Kaiepi