Verwenden Sie Haskell-ähnliche Prelude-Module in einem Modul in Raku

11

Ich schreibe ein Zeichenpaket mit einigen Teilen und habe Operatoren und Datentypen, die überall verteilt sind. Ich möchte jedoch nicht, dass die Benutzer jedes Mal die entsprechenden Module hinzufügen, da dies ziemlich chaotisch wäre, zum Beispiel hätte ich eine PointKlasse, eine MonoidRolle und eine StyleKlasse in verschiedenen Pfaden wie diesen

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

Ich hätte gerne einen haskellähnlichen Auftakt lib/Package/Prelude.pm6 mit dem Effekt, dass ich solche Skripte schreiben kann

use Package::Prelude;

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

anstatt zu tun

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

Ich habe viele Dinge ausprobiert:

  • Diese Version gibt mir nicht den richtigen Effekt, ich muss den gesamten Pfad eingeben, um zu zeigen, dh Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • Diese Version gibt mir Pointsofort das, aber ich bekomme Probleme mit den Operatoren und so weiter, außerdem möchte ich einfach automatisch alles aus den exportierten Routinen in den genannten Beispielpaketen hinzufügen.
# $?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
     ...
}

Kennen Sie einen besseren und schnelleren Weg, um eine solche Vorspiel-ähnliche Datei zu erhalten?

Margolari
quelle
Sie können verwenden unit class Package::Data::Point. Sie müssen nicht verwenden module.
Brad Gilbert

Antworten:

12

Die Verwendung EXPORTist in die richtige Richtung. Die wichtigsten Dinge zu wissen sind:

  • Importe sind lexikalisch
  • Wir können Introspektion verwenden, um die Symbole im aktuellen lexikalischen Bereich zu erhalten und darauf zuzugreifen

Das Rezept lautet also:

  • use alle Module innerhalb von EXPORT
  • Extrahieren Sie dann alle importierten Symbole und geben Sie sie als Ergebnis von zurück EXPORT

Als Beispiel erstelle ich ein Modul Foo::Point, das einen Operator und eine Klasse enthält:

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

Und nur um zu demonstrieren, dass es mit mehreren Modulen funktionieren kann, auch a Foo::Monad:

unit module Foo::Monad;

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

Das Ziel ist es, diese Arbeit zu machen:

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

Was erreicht werden kann, indem man ein Schreiben schreibt Foo::Prelude, das Folgendes enthält:

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

Hier gibt es ein paar Kuriositäten zu erklären:

  1. A subhat implizite Erklärungen $_, $/und $!. Das Exportieren dieser würde zu einem Kollisionsfehler bei der Kompilierungszeit führen, wenn das Modul use'd' ist. Ein Block hat nur eine implizite $_. So erleichtern wir uns das Leben mit einem verschachtelten nackten Block.
  2. Damit grepsoll sichergestellt werden, dass wir unser implizit deklariertes $_Symbol nicht exportieren (dank des verschachtelten Blocks ist es das einzige, um das wir uns kümmern müssen).
  3. ::ist eine Möglichkeit, auf den aktuellen Bereich zu verweisen (Etymologie: ::ist das Pakettrennzeichen). ::.pairsAuf diese Weise werden PairObjekte für jedes Symbol im aktuellen Bereich erhalten.

Es gibt einen spekulierten Re-Export-Mechanismus, der möglicherweise in einer zukünftigen Raku-Sprachversion erscheint und die Notwendigkeit für dieses Stück Boilerplate eliminiert.

Jonathan Worthington
quelle
Schließlich ist dies genau das Verhalten, nach dem ich gesucht habe, vielen Dank!
Margolari