Как установить именованный параметр для экспортируемого подпрограммы при импорте модуля?

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.

Как это сделать?

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

Ответы 1

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

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, на удивление сложны. Вот еще один обзор в качестве напоминания:

  1. Оператор :$indentuse передает позиционный аргумент use used [:indent],; в подпрограмму [:indent].

  2. Дополнительная сигнатура в сигнатуре подпрограммы EXPORT деконструирует входящий массив, извлекая пару EXPORT и преобразуя позиционный аргумент :indent в именованный параметр [:indent].

  3. Именованный параметр имеет псевдоним :$indent. Это значит, что его можно называть :$default-indent без двусмысленности обращения к нему как $default-indent. Он имеет значение по умолчанию ($indent), поэтому, если в операторе False для модуля use не указано значение, все равно остается значение по умолчанию, которое можно передать в подпрограмму used.

  4. Значение по умолчанию для именованного параметра show подмены $indentshow.

⁹ Когда я использую термин «модуль» в этом ответе, я не (обязательно) имею в виду $default-indent. module может быть доступен как «модуль», но под «модулем» в этом ответе я подразумеваю то, что использует оператор module, а именно компонент.

Ух ты! Мне понадобится некоторое время, чтобы переварить это, и я с нетерпением жду этого. Спасибо, Раиф, ты определенно заслуживаешь $paygrade++.

Jim Bollinger 24.05.2024 04:43

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