ГЛАВНАЯ подпрограмма

Сценарий

Представьте, что у вас есть модуль X, функции которого доступны пользователю через интерфейс командной строки. Такой модуль сам по себе мало что делает, но он позволяет другим людям создавать модули, похожие на плагины, которые они могут подключать к модулю X. В идеале плагины можно было бы использовать через интерфейс командной строки X.

Таким образом, мой вопрос:

Что вам нужно сделать, чтобы подключить любой функционал плагин может предоставлять интерфейс командной строки X?

Это означает, что плагин должен будет предоставить некоторую структуру, описывающую команду, то, что нужно вызвать, и, надеюсь, сообщение об использовании для плагина. Поэтому, когда вы запускаете интерфейс командной строки X, команда плагина и справочное сообщение отображаются в обычном справочном сообщении интерфейса командной строки X.

Пример

main.p6:

use Hello;
use Print;

multi MAIN('goodbye') {
    put 'goodbye'
}

lib/Hello.pm6:

unit module Hello;

our %command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);

lib/Print.pm6:

unit module Print;

our %command =  %(
    command => 'print',
    routine => sub { .print for 1..10 },
    help    => 'print numbers 1 through 10.'
);

Программа main.p6 представляет собой упрощенную версию этого сценария. Я хотел бы интегрировать информацию, предоставленную как Hello.pm6, так и Print.pm6 через их соответствующие переменные %command в две новые подпрограммы MAIN в main.p6.

Это возможно? Если да, то как мне добиться этого?

Я попытался экспортировать новые мультифункции MAIN из модулей, но получил ошибку: An anonymous routine may not take a multi declarator. Если я не использовал multi, я получил ошибку: Redeclaration of routine 'MAIN'... Так что, может быть, их нельзя добавить динамически?

Håkon Hægland 15.07.2019 16:39

Основная линия @HåkonHægland настраивается только один раз (при первом обнаружении) - если вы импортируете MAIN из двух разных модулей, будет использоваться только один.

ugexe 15.07.2019 17:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
2
130
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это выглядит довольно специфично для вопроса StackOverflow, но я все равно попробую. Здесь есть несколько проблем. Первый — зарегистрировать команды как таковые, чтобы MAIN мог выдать сообщение о том, что «это делает то», а второй — фактически выполнить команду. Если оба могут быть известны во время компиляции, это, вероятно, можно исправить. Но давайте посмотрим, как будет работать реальный код. Я просто сделаю первую часть, а остальное оставлю в качестве упражнения.

Во-первых, %command нужно как-то экспортировать. Вы не можете сделать это так, как вы делаете это сейчас. Во-первых, потому что он явно не экспортируется; если бы это было так, вы бы получили несколько символов с одним и тем же именем во внешней области. Поэтому нам нужно изменить это на класс, чтобы фактические символы были лексическими для класса и не загрязняли внешнюю область видимости.

unit class Hello;

has %.command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);

(То же самое относится и к Print)

Пока у нас есть это, остальное не так сложно, только мы должны использовать самоанализ, чтобы узнать, что там на самом деле, как небольшой хак:

use Hello;
use Print;

my @packages=  MY::.keys.grep( /^^<upper> <lower>/ );
my @commands = do for @packages -> $p {
    my $foo = ::($p).new();
    $foo.command()<command>
};

multi MAIN( $command where * eq any(@commands) ) {
    say "We're doing $command";
}

Мы проверяем таблицу символов в поисках пакетов, начинающихся с заглавной буквы, за которой следует другая не заглавная буква. Так получилось, что нас интересуют только пакеты, но, конечно, если вы хотите использовать это как механизм плагина, вы должны просто использовать любой шаблон, который будет уникальным для них. Затем мы создаем экземпляры этих новых пакетов и вызываем автоматически сгенерированный метод команды, чтобы получить имя команды. И это именно то, что мы используем, чтобы проверить, правильно ли мы выполняем команду в подпрограмме MAIN, используя подпись where, чтобы проверить, действительно ли используемая строка находится в списке известных команд.

Так как функции и все остальное доступно также из @packages, их фактический вызов (или отправка дополнительного сообщения или что-то еще) остается в качестве упражнения.

Обновлять: вы можете попробовать этот другой ответ StackOveflow как альтернативный механизм для подписей в модулях.

Другие вопросы по теме