unit module My::Show;
sub show (:$indent = False) is export {
if $indent {
say " Showing with indentation.";
} else {
say "Showing without indentation.";
}
}
При импорте этого кода я хотел бы указать любой из этих трех:
use My::Show :indent;
use My::Show :!indent;
use My::Show;
Уточнение (спасибо @raiph): параметр :$indent подмены show имеет значение по умолчанию. Пользователь модуля должен иметь возможность указать значение по умолчанию в строке use.
Как это сделать?





TL;DR Вам нужно написать и соответствующим образом вызвать подпрограмму EXPORT.¹²³⁴⁵⁶⁷
used.rakumod:
sub EXPORT ([:indent($default-indent) = False]) {
sub show (:$indent = $default-indent) is export { $indent }
Map.new: '&show' => &EXPORT::DEFAULT::show;
}
main.raku:
use lib '.';
use used [:indent],;
say show; # True
used.rakumodЕсли модуль⁹ — это used и у него есть подпрограмма EXPORT (на уровне компьютера), то:
Саб EXPORT называется.
Любые позиционные аргументы, предоставленные оператором use, передаются в подпрограмму EXPORT, и это можно использовать для достижения желаемого эффекта в пределах разумного.⁴⁵⁶⁷
Модуль used — это не что иное, как подпрограмма EXPORT. Это начинается:
sub EXPORT ([:indent($default-indent) = False]) {
Подпись и ее поведение требуют объяснения:
EXPORT сабвуферы привязаны только к позиционным аргументам.⁴
Очевидно, желательно иметь возможность указать значение по умолчанию для именованного аргумента show, используя то, что выглядит как можно более близким к передаче того же именованного аргумента. Я имитирую это, написав [:indent] в заявлении use used [:indent],;.⁴
Учитывая, что переданный позиционный аргумент является массивом, подпрограмме EXPORT необходимо деконструировать этот позиционный аргумент, чтобы извлечь внутренний аргумент внутри внешнего позиционного аргумента. Это то, что делает [...]; он ведет себя как дополнительная сигнатура , которая «деконструирует» аргумент позиционного массива.
Параметр деконструкции :indent — это именованный параметр, превращающий то, что нам нужно было передать как элемент позиционного массива, обратно в нужную нам форму в подпункте EXPORT.
Кроме того, я воспользовался преимуществом наличия настоящего именованного параметра, чтобы присвоить ему псевдоним: :$default-index. (Вы записываете псевдонимы параметров, записывая их в форме :foo(:bar(:$baz)), так что только внутренний идентификатор/псевдоним имеет сигил, хотя в результате вы получаете псевдонимы $foo, $bar и $baz.)
Указание псевдонима устраняет проблему, которую в любом случае придется устранить: хотя мой подход позволяет писать :indent как часть оператора use, для :$indent должен быть псевдоним, потому что в противном случае мы не сможем сделать его значением по умолчанию для подменю show. :$indent параметр:
sub show (:$indent = $default-indent) is export { $indent }
Надеюсь, вы понимаете, почему это не сработало бы, если бы я написал :$indent = $indent в качестве подписи show.
Вышеописанное может показаться довольно сложным. (Потому что это так.) Я написал еще одно краткое изложение в еще одной сноске, которая может помочь.⁸
Наконец есть:
Map.new: '&show' => &EXPORT::DEFAULT::show
Я буду полагаться на соответствующий документ, который поможет вам понять, что вам нужно знать об этой строке. Я предлагаю вам проигнорировать это при первом прочтении этого ответа.
(В частности, вас могут сбить с толку двойные упоминания значений по умолчанию — $default-index и DEFAULT. Достаточно сказать, что они не имеют ничего общего друг с другом.)
main.rakuОсталось только use установить модуль⁹ и вызвать show.
Надеюсь, вы знаете, что строка use lib '.'; в моем main.raku предназначена только для того, чтобы код работал без необходимости объяснять (кому-то, кто вырезает и вставляет мой код для его запуска), как настроить и использовать каталог библиотеки.
Следующая строка — это оператор use, который передает позиционный аргумент, как обсуждалось ранее:
use used [:indent],;
Наконец, доказательство того, что это работает (хотя оно и проверено очень слабо!):
say show; # True
¹ Я пишу этот ответ с авторитетным видом, как будто это определенно полезно и правильно. Я считаю, что это помогает облегчить чтение. Однако реальность такова, что если мое предположение в моем комментарии к вашему вопросу о том, чего вы хотите, неверно, этот ответ, скорее всего, будет бесполезен. И даже если бы моя догадка была примерно верной, этот ответ все равно может оказаться бесполезным, потому что то, о чем он идет, близко к грани моего понимания и компетентности, и я, возможно, перешагнул через край, а может быть, даже дико выворачиваю ноги на полпути. через мультяшный каньон, еще не осознавая, что я здесь.
² Я просто предоставляю самое простое доказательство концепции, которое я могу придумать, чтобы сделать то, что, по моему мнению, вы хотите сделать, а также многочисленные сноски, поясняющие оговорки и т. д.
³ Подпрограмма EXPORT — это самый мощный из стандартных механизмов управления «экспортом и выборочным импортом». Возможно, вас удивит то, что я советую вам использовать инструмент экспорта, особенно тяжелый. Похоже, то, что вам нужно, не связано с экспортом — у вас уже есть is export. Разве этого не должно быть достаточно? И то, что вы хотите, не похоже на выборочный импорт. Вы просто хотите импортировать то, что модуль был доступен для экспорта, как обычно, хотя и объявляя параметр по умолчанию для одного из экспортированных sub. Это не может быть так сложно, правда? Достаточно сказать, что sub EXPORT — это механизм, с помощью которого можно делать самые причудливые вещи, которые только можно сделать во время экспорта/импорта, и, насколько я сейчас понимаю, то, что вы хотите сделать, требует достаточно сложной работы ног, поэтому действительно требуется sub EXPORT.
⁴ В стандартном Raku любая пара (именованный аргумент), указанная на верхнем уровне оператора use (например, use module :indent;), будет интерпретироваться как «тег» экспорта/импорта. (Это объясняет, почему только позиционные параметры на верхнем уровне сигнатуры EXPORT subs могут быть связаны с чем-либо.) Кроме того, обычные способы запретить Raku рассматривать пару как именованный аргумент и вместо этого рассматривать ее как позиционный аргумент (например, с помощью поместив его в круглые скобки), не работайте с оператором use. Вместо этого вам нужно сделать что-то, как я: поместить пару в литерал массива ([:indent]) плюс добавить дополнительную запятую вне литерала массива, чтобы поместить ее во внешний список ([:indent],)).⁵
⁵ Вы можете просто отказаться от идеи передачи пары и вместо этого просто передать непарное значение, например. use module 99;. (И измените получающую подпрограмму EXPORT так, чтобы она соответствовала.) Но я понимаю, почему вы хотели использовать пару, имя/ключ которой точно соответствует именованному параметру, для которого вы хотели указать значение по умолчанию, поэтому я показал вам, что вам нужно сделать. чтобы это сработало в обычных обстоятельствах.⁶
⁶ Raku, в принципе, является произвольно программируемым языком (с поддержкой пользовательской модификации синтаксиса и/или семантики Raku). Таким образом, в принципе вы могли бы создать модуль/прагму (или использовать существующий, если таковой существовал), который изменил бы Raku так, чтобы операторы use после использования установленного модуля/прагмы позволяли передавать именованные аргументы верхнего уровня в подпрограмму EXPORT. Тогда вы, вероятно, могли бы написать use module :indent; или что-то подобное вместо того, что я придумал. Но я считаю, что дальнейшее обсуждение такого подхода выходит далеко за рамки разумного ответа на ваш вопрос и моей зарплаты.
⁷ Обратной стороной именованных аргументов оператора use, являющихся тегами экспорта/импорта, является то, что любые позиционные аргументы оператора use передаются в подпрограмму use модуля EXPORTd.
⁸ Механизмы, которые я предпринял, чтобы иметь возможность указать значение по умолчанию для параметра show sub, на удивление сложны. Вот еще один обзор в качестве напоминания:
Оператор :$indentuse передает позиционный аргумент use used [:indent],; в подпрограмму [:indent].
Дополнительная сигнатура в сигнатуре подпрограммы EXPORT деконструирует входящий массив, извлекая пару EXPORT и преобразуя позиционный аргумент :indent в именованный параметр [:indent].
Именованный параметр имеет псевдоним :$indent. Это значит, что его можно называть :$default-indent без двусмысленности обращения к нему как $default-indent. Он имеет значение по умолчанию ($indent), поэтому, если в операторе False для модуля use не указано значение, все равно остается значение по умолчанию, которое можно передать в подпрограмму used.
Значение по умолчанию для именованного параметра show подмены $indent — show.
⁹ Когда я использую термин «модуль» в этом ответе, я не (обязательно) имею в виду $default-indent. module может быть доступен как «модуль», но под «модулем» в этом ответе я подразумеваю то, что использует оператор module, а именно компонент.
Ух ты! Мне понадобится некоторое время, чтобы переварить это, и я с нетерпением жду этого. Спасибо, Раиф, ты определенно заслуживаешь $paygrade++.