Используйте Haskell как модули Prelude в модуле в raku

11

Я пишу чертежный пакет с некоторыми частями, и у меня есть операторы и типы данных, разбросанные по всему. Однако я не хочу, чтобы пользователи добавляли соответствующие модули каждый раз, так как это было бы довольно грязно, например, у меня был бы Pointкласс, Monoidроль и Styleкласс по разным путям, как этот

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

Я хотел бы иметь haskellподобное lib/Package/Prelude.pm6 вступление с эффектом, что я могу писать такие сценарии

use Package::Prelude;

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

вместо того, чтобы делать

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

Я перепробовал много вещей:

  • Эта версия не дает мне нужного эффекта, я должен набрать весь путь до точки, то есть Package::Data::Point...
unit module Package::Prelude;
# $?FILE = lib/Package/Prelude.pm6
use Package::Data::Style;
use Package::Data::Point;
use Package::Data::Monoid;
  • Эта версия дает мне Pointправо сразу, но у меня возникают проблемы с операторами и так далее, также я хотел бы просто автоматически добавить все из экспортированных подпрограмм в упомянутые примеры пакетов.
# $?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
     ...
}

Знаете ли вы, люди, лучший и быстрый способ получить такой прелюдоподобный файл?

margolari
источник
Вы можете использовать unit class Package::Data::Point. Вам не нужно использовать module.
Брэд Гилберт

Ответы:

12

Использование EXPORTв правильном направлении. Ключевые вещи, которые нужно знать:

  • Импорт лексический
  • Мы можем использовать интроспекцию для получения и доступа к символам в текущей лексической области видимости.

Итак, рецепт таков:

  • use все модули внутри EXPORT
  • Затем извлеките все импортированные символы и верните их в результате EXPORT

В качестве примера я создаю модуль Foo::Point, включающий оператор и класс:

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

И, просто чтобы продемонстрировать, что он может работать с несколькими модулями, также Foo::Monad:

unit module Foo::Monad;

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

Цель состоит в том, чтобы сделать эту работу:

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

Что может быть достигнуто путем написания, Foo::Preludeкоторый содержит:

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

Здесь есть несколько странностей, чтобы объяснить:

  1. subИмеет неявные декларации $_, $/и $!. Их экспорт может привести к ошибке конфликта символов во время компиляции, когда модуль имеет значение use«d». Блок имеет только неявный $_. Таким образом, мы делаем нашу жизнь проще с вложенным голым блоком.
  2. Это grepсделано для того, чтобы мы не экспортировали наш неявно объявленный $_символ (благодаря вложенному блоку, он единственный, о котором нам нужно заботиться).
  3. ::способ ссылки на текущую область (этимология: ::разделитель пакетов) ::.pairsтаким образом получает Pairобъекты для каждого символа в текущей области видимости.

Существует предполагаемый механизм реэкспорта, который может появиться в будущем выпуске языка Raku, который устранит необходимость в этом кусочке.

Джонатан Уортингтон
источник
Наконец, это именно то поведение, которое я искал, большое спасибо!
Марголари