Use os módulos Haskell como Prelude em um módulo no raku

11

Estou escrevendo um pacote de desenho com algumas peças e tenho operadores e tipos de dados espalhados por toda parte. No entanto, eu não quero que os usuários adicionem os módulos correspondentes todas as vezes, pois seria muito complicado, por exemplo, eu teria uma Pointclasse, uma Monoidfunção e uma Styleclasse em caminhos diferentes como este

unit module Package::Data::Monoid;
# $?FILE = lib/Package/Data/Monoid.pm6

role Monoid {...}
unit module Package::Data::Point;
# $?FILE = lib/Package/Data/Point.pm6

class Point {...}
unit module Package::Data::Style;
# $?FILE = lib/Package/Data/Style.pm6

class Style {...}

Eu gostaria de ter um haskellprelúdio semelhante lib/Package/Prelude.pm6 com o efeito de que eu posso escrever esses scripts

use Package::Prelude;

# I can use Point right away, Style etc...

em vez de fazer

use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

# I can too use point right away, but for users not knowing the
# inner workings it's too overwhelming

Eu tentei muitas coisas:

  • Esta versão não me dá o efeito certo, tenho que digitar todo o caminho a apontar, ou seja, Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • Esta versão me fornece o Pointimediato, mas tenho problemas com os operadores e assim por diante. Também gostaria de adicionar automaticamente tudo, desde as rotinas exportadas nos pacotes de exemplo mencionados.
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;

sub EXPORT {
  hash <Point> => Point
     , <Style> => Style
     , <mappend> => &mappend
     ...
}

Vocês conhecem uma maneira melhor e rápida de obter um arquivo como um prelúdio?

margolari
fonte
Você pode usar unit class Package::Data::Point. Você não precisa usar module.
Brad Gilbert

Respostas:

12

O uso EXPORTestá na direção certa. As principais coisas a saber são:

  • As importações são lexicais
  • Podemos usar a introspecção para obter e acessar os símbolos no escopo lexical atual

Então a receita é:

  • use todos os módulos dentro de EXPORT
  • Em seguida, extraia todos os símbolos importados e retorne-os como resultado de EXPORT

Como exemplo, eu crio um módulo Foo::Point, incluindo um operador e uma classe:

unit module Foo::Point;

class Point is export {
    has ($.x, $.y);
}

multi infix:<+>(Point $a, Point $b) is export {
    Point.new(x => $a.x + $b.x, y => $a.y + $b.y)
}

E, apenas para demonstrar que pode funcionar com vários módulos, também Foo::Monad:

unit module Foo::Monad;

class Monad is export {
    method explain() { say "Just think of a burrito..." }
}

O objetivo é fazer com que isso funcione:

use Foo::Prelude;
say Point.new(x => 2, y => 4) + Point.new(x => 3, y => 5);
Monad.explain;

O que pode ser alcançado escrevendo um Foo::Preludeque contém:

sub EXPORT() {
    {
        use Foo::Point;
        use Foo::Monad;
        return ::.pairs.grep(*.key ne '$_').Map;
    }
}

Há algumas esquisitices aqui para explicar:

  1. Um subtem declarações implícitas de $_, $/e $!. A exportação dessas informações resultaria em um erro de conflito de símbolo em tempo de compilação quando o módulo é use'd. Um bloco tem apenas um implícito $_. Assim, facilitamos nossa vida com um bloco vazio aninhado.
  2. O grepé para garantir que não exportamos nosso $_símbolo declarado implicitamente (graças ao bloco aninhado, é o único com o qual precisamos nos preocupar).
  3. ::é uma maneira de referenciar o escopo atual (etimologia: ::é o separador de pacotes). ::.pairsassim obtém Pairobjetos para cada símbolo no escopo atual.

Existe um mecanismo de reexportação especulado que pode aparecer em uma versão futura da linguagem Raku que eliminaria a necessidade desse pedaço de clichê.

Jonathan Worthington
fonte
Finalmente, esse é exatamente o comportamento que eu estava procurando, muito obrigado!
margolari 13/04