¿Cómo puedes escribir un módulo para un grupo de roles?

8

Una característica de los roles son los grupos de roles, que le permiten declarar múltiples roles con el mismo nombre aceptando diferentes parámetros, de manera similar a las rutinas múltiples:

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

A menudo, para un tipo, tiene un módulo. El problema es que solo puede tener una unitdeclaración de alcance en un módulo, por lo que no puede usarla con grupos de roles. ¿Cómo puedes escribir un módulo para un grupo de roles?

Kaiepi
fuente

Respuestas:

9

Puede tener módulos sin unitdeclaraciones de ámbito y exportar símbolos de él, pero la forma de exportar un grupo de roles es un poco problemático. No puede usar el is exportrasgo para esto, ya que exportará los tipos incorrectos. Cuando hace referencia a un rol después de que se ha declarado, se refiere al grupo de roles, no a los roles individuales dentro de él, pero el uso is exportde roles individuales exportará esos roles individuales, no el grupo de roles. ¡Los roles individuales tienen un CÓMO muy diferente al de los grupos de roles y no se comportarán como lo esperarías normalmente!

Afortunadamente, hay una manera de hacer esto usando el EXPORTpaquete. Declarar el Foogrupo de roles en este paquete le dará un nombre EXPORT::DEFAULT::Foo, que probablemente no desee, por lo que querrá declararlo en el MYalcance de la unidad y declarar una constante para él en su EXPORT::DEFAULTlugar:

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

Ahora Foose puede importar y usar OK:

use Foo;

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

Nota: no puede usar ::nombres constantes, por lo que para exportar un grupo de roles en un espacio de nombres deberá envolverlo en otro paquete:

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

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

Simple, solo use el ouralcance predeterminado sin unitnada alrededor .

unitSólo se añadió de modo que usted no tiene que rodear un archivo completo con {y }cuando sólo había una module, package, class, role, o suben el archivo.
No siempre tienes que usarlo.
De hecho, nunca tienes que usarlo.


Si lo desea, agregue una declaración directa sin una parametrización.
Un rasgo agregado generalmente se aplicará a todos los roles con el mismo nombre.

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

Luego, cuando lo usa, se carga automáticamente de tal manera que sea accesible por nombre completo.

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

En este caso, puede ponerlo dentro de una declaración de módulo para que no necesite escribir con Foo::tanta frecuencia.

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

El rol sigue siendo accesible como Foo::Bar.
No me sorprendería si esto resultara exactamente el mismo código que el ejemplo anterior.

La única razón para agregar is exportes si desea que se exporte como en Barlugar de Foo::Bar. Esto se aplica a los dos ejemplos anteriores.


Supongo que pensaste que siempre necesitabas usar is export. En muchos casos, realmente no lo haces.

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`

Si desea poder usarlo baz()mientras lo tiene dentro del alcance del módulo, entonces y solo entonces necesita exportarlo.

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

Tenga en cuenta que todavía lo declare así, de ourmodo que si alguien no quiere que lo exporte, aún puede acceder a él.

use Foo::Bar ();

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

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

El propósito de is exportes eliminar la necesidad de usar nombres totalmente calificados para las funciones. Que también funcione para cosas como los roles es un beneficio secundario.

Brad Gilbert
fuente
Ah, pensé que ourlas declaraciones con alcance en módulos sin unitdeclaraciones con alcance se convirtieron en globales por alguna razón. Mencioné el comportamiento con is exportporque supuse que así es como la gente podría intentar exportar algo como esto.
Kaiepi
Creo que unit module Foodebería ser así unit package Foo. Si se trata de un módulo, entonces si hay alguna existente Fooen otros lugares, a continuación, los símbolos serán no combinar adecuadamente si ambos Foo::Bary Fooson importados.
Kaiepi