Как вы можете написать модуль для группы ролей? - PullRequest
8 голосов
/ 27 марта 2020

Особенностью ролей являются группы ролей, которые позволяют объявлять несколько ролей с одним и тем же именем, принимающих разные параметры, аналогично многим подпрограммам:

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)  { }
}

Часто для одного типа у вас есть один модуль , Проблема в том, что в модуле может быть только одно объявление unit, поэтому вы не можете использовать его с группами ролей. Как вы можете написать модуль для группы ролей?

Ответы [ 2 ]

9 голосов
/ 27 марта 2020

У вас могут быть модули без unit объявлений с областями видимости и экспортом символов из них, но то, как вы экспортируете группу ролей, представляет собой небольшую проблему. Вы не можете использовать черту is export для этого, так как это будет экспортировать неправильные типы. Когда вы ссылаетесь на роль после того, как она была объявлена, вы имеете в виду группу ролей, а не отдельные роли в ней, а использование is export для отдельных ролей экспортирует эти отдельные роли, а не группу ролей. Отдельные роли имеют HOW очень отличный от групп ролей и не будут вести себя так, как вы обычно ожидаете от ролей!

К счастью, есть способ сделать это с помощью пакета EXPORT. Объявление группы ролей Foo в этом пакете даст ей имя EXPORT::DEFAULT::Foo, которое вам, вероятно, не нужно, поэтому вы захотите объявить ее в области действия MY модуля и объявить константу для вместо EXPORT::DEFAULT:

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;
}

Теперь Foo можно импортировать и использовать ОК:

use Foo;

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

Примечание: вы не можете использовать :: в именах констант, поэтому для экспорта группы ролей в пространство имен вам нужно обернуть ее в другой пакет:

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

my package EXPORT::DEFAULT {
    package Foo {
        constant Bar = Foo::Bar;
    }
}
3 голосов
/ 01 апреля 2020

Простой, просто используйте область по умолчанию our без окружения unit что-либо.

unit был добавлен только для того, чтобы вам не пришлось окружать весь файл { и } когда в файле был только один module, package, class, role или sub.
Вам не всегда нужно его использовать.
На самом деле у вас никогда не будет для его использования.


Если вы хотите, добавьте предварительное объявление без параметризации.
Добавленная к нему черта обычно применяется ко всем ролям. с тем же именем.

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)  { }
}

Затем, когда вы используете его, он автоматически загружается таким образом, что он доступен по полному имени.

{
    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'

В этом случае вы можете поместить его в оператор модуля, чтобы вам не приходилось так часто писать Foo::.

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)  { }
}

Роль по-прежнему доступна как Foo::Bar.
Я не удивлюсь, если это приведет к тому же коду, что и в предыдущем примере.

* 10 40 * Единственная причина для добавления is export состоит в том, что вы хотите экспортировать его как Bar вместо Foo::Bar. Это относится к обоим приведенным выше примерам.

Полагаю, вы думали, что вам всегда нужно было использовать is export. Во многих случаях вы действительно этого не сделаете.

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`

Если вы хотите иметь возможность просто использовать baz(), находясь внутри области действия модуля, тогда и только затем вам нужно его экспортировать.

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

Обратите внимание, что я все еще объявил его как our, так что если кто-то не хочет, чтобы вы его экспортировали, он все еще был доступен к ним.

use Foo::Bar ();

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

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

Вся цель is export состоит в том, чтобы устранить необходимость использования полностью определенных имен для функций. То, что это также работает для таких вещей, как роли, является дополнительным преимуществом.

...