Używaj Haskell jak modułów Preludium w module w raku

11

Piszę pakiet rysunków z niektórymi częściami, a operatorzy i typy danych są rozproszone po całej. Jednak nie chcę, aby użytkownicy dodawali odpowiednie moduły za każdym razem, ponieważ byłoby to dość bałagan, na przykład miałbym Pointklasę, Monoidrolę i Styleklasę na różnych ścieżkach, takich jak ten

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

Chciałbym mieć haskellpodobne preludium lib/Package/Prelude.pm6 z efektem, że mogę pisać takie skrypty

use Package::Prelude;

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

zamiast robić

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

Próbowałem wielu rzeczy:

  • Ta wersja nie daje mi właściwego efektu, muszę wpisać całą ścieżkę do punktu, tj. Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • Ta wersja daje mi od Pointrazu, ale mam problemy z operatorami i tak dalej, chciałbym również automatycznie dodać wszystko z eksportowanych procedur we wspomnianych przykładowych pakietach.
# $?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
     ...
}

Czy wy, ludzie, znacie lepszy i szybki sposób na uzyskanie takiego pliku przypominającego preludium?

margolari
źródło
Możesz użyć unit class Package::Data::Point. Nie musisz używać module.
Brad Gilbert

Odpowiedzi:

12

Korzystanie EXPORTjest w dobrym kierunku. Najważniejsze rzeczy, które należy wiedzieć:

  • Import jest leksykalny
  • Możemy użyć introspekcji, aby uzyskać i uzyskać dostęp do symboli w bieżącym zakresie leksykalnym

Przepis jest następujący:

  • use wszystkie moduły wewnątrz EXPORT
  • Następnie wyodrębnij wszystkie zaimportowane symbole i zwróć je jako wynik EXPORT

Jako przykład tworzę moduł Foo::Pointzawierający operator i klasę:

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

I tylko w celu zademonstrowania, że ​​może współpracować z wieloma modułami, a także Foo::Monad:

unit module Foo::Monad;

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

Celem jest, aby to działało:

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

Co można osiągnąć, pisząc, Foo::Preludeże zawiera:

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

Można tu wyjaśnić kilka osobliwości:

  1. subMa ukryte oświadczeń $_, $/oraz $!. Wyeksportowanie ich spowoduje błąd kolizji symbolu podczas kompilacji, gdy moduł ma wartość used. Blok ma tylko domniemanie $_. W ten sposób ułatwiamy nasze życie za pomocą zagnieżdżonego pustego bloku.
  2. Ma grepto na celu upewnienie się, że nie eksportujemy naszego niejawnie zadeklarowanego $_symbolu (dzięki zagnieżdżonemu blokowi jest to jedyny, na którym musimy się przejmować).
  3. ::jest sposobem na odniesienie do bieżącego zakresu (etymologia: ::jest separatorem pakietów). ::.pairsw ten sposób uzyskuje Pairobiekty dla każdego symbolu w bieżącym zakresie.

Istnieje spekulowany mechanizm reeksportu, który może pojawić się w przyszłej wersji językowej Raku, który wyeliminowałby potrzebę korzystania z tego fragmentu.

Jonathan Worthington
źródło
Wreszcie, właśnie takiego zachowania szukałem, dziękuję bardzo!
margolari