Como você pode escrever um módulo para um grupo de funções?

8

Um recurso de funções são os grupos de funções, que permitem declarar várias funções com o mesmo nome, aceitando parâmetros diferentes, da mesma forma que as várias rotinas:

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

Frequentemente, para um tipo, você tem um módulo. O problema é que você só pode ter uma unitdeclaração de escopo em um módulo, portanto não pode usá-la com grupos de funções. Como você pode escrever um módulo para um grupo de funções?

Kaiepi
fonte

Respostas:

9

Você pode ter módulos sem unitdeclarações no escopo e exportar símbolos a partir dele, mas como exportar um grupo de funções é um pouco problemático. Você não pode usar a is exportcaracterística para isso, pois isso exportará os tipos errados. Quando você se refere a uma função após sua declaração, você está se referindo ao grupo de funções, não às funções individuais dentro dela, mas o uso is exportde funções individuais exportará essas funções individuais, não o grupo de funções. Papéis individuais têm um COMO muito diferente dos grupos de papéis e não se comportarão como você normalmente esperaria!

Felizmente, há uma maneira de fazer isso usando o EXPORTpacote. Declarar o Foogrupo de funções neste pacote fornecerá um nome EXPORT::DEFAULT::Foo, do qual você provavelmente não deseja, portanto, você deve declará-lo no MYescopo da unidade e, em EXPORT::DEFAULTvez disso, declarar uma constante para ele :

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

Agora Foopode ser importado e usado 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: você não pode usar ::em nomes constantes, portanto, para exportar um grupo de funções em um espaço para nome, será necessário agrupá-lo em outro pacote:

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

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

Simples, basta usar o ourescopo padrão sem envolver unitnada.

unitsó foi adicionado para que você não teria que cercam um arquivo inteiro com {e }quando havia apenas um module, package, class, role, ou subno arquivo.
Você nem sempre precisa usá-lo.
Na verdade, você nunca precisa usá-lo.


Se desejar, adicione uma declaração direta sem uma parametrização.
Uma característica adicionada a ela geralmente se aplica a todas as funções com o mesmo nome.

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

Então, quando você o usa, ele é carregado automaticamente de forma que seja acessível por nome totalmente qualificado.

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

Nesse caso, você pode colocá-lo dentro de uma instrução de módulo para não precisar escrever com Foo::tanta frequência.

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

O papel ainda está acessível como Foo::Bar.
Eu não ficaria surpreso se isso resultasse exatamente no mesmo código do exemplo anterior.

O único motivo para adicionar is exporté se você deseja que ele seja exportado como em Barvez de Foo::Bar. Isso se aplica aos dois exemplos acima.


Meu palpite é que você pensou que sempre precisava usar is export. Em muitos casos, você realmente não.

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`

Se você quisesse usá- baz()lo apenas dentro do escopo do módulo, e somente então precisará exportá-lo.

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

Observe que eu ainda o declarei, de ourmodo que, se alguém não quiser que você o exporte, ele ainda estará acessível a eles.

use Foo::Bar ();

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

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

O objetivo de todo is exporté remover a necessidade de usar nomes totalmente qualificados para funções. Que ele também funcione para coisas como papéis é um benefício colateral.

Brad Gilbert
fonte
Ah, pensei que ourdeclarações com escopo definido em módulos sem unitdeclarações com escopo se tornassem globais por algum motivo. Eu mencionei o comportamento is exportporque presumi que é assim que as pessoas podem primeiro tentar exportar algo assim.
Kaiepi 01/04
Eu acho que unit module Foodeveria ser unit package Fooembora. Se for um módulo, se houver algum Foooutro lugar, os símbolos não serão mesclados corretamente se ambos Foo::Bare Fooforem importados.
Kaiepi 01/04