Используйте Haskell как модули Prelude в модуле в raku - PullRequest
11 голосов
/ 13 апреля 2020

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

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

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

1 Ответ

12 голосов
/ 13 апреля 2020

Использование 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. A sub имеет неявные объявления $_, $/ и $!. Их экспорт приведет к ошибке символа sh времени компиляции, когда модуль use 'd. Блок имеет только неявный $_. Таким образом, мы облегчаем нашу жизнь с помощью вложенного пустого блока.
  2. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * grep, чтобы не экспортировать наш неявно объявленный символ $_ (благодаря вложенному блоку, это единственный надо заботиться).
  3. :: - это способ ссылки на текущую область (этимология: :: - разделитель пакетов). Таким образом, ::.pairs получает Pair объектов для каждого символа в текущей области видимости.

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

...