Сохранение значений по умолчанию для вложенных именованных параметров

Я подозреваю, что на этот вопрос очень легко ответить, а ответ - нет. Однако я хочу убедиться, что ничего не упускаю.

Рассмотрим следующий код:

sub f(:$a = 'foo') { say $a }
sub g(:$a) { f :$a }
g();  # OUTPUT: «(Any)»

Есть ли хороший способ изменить подпись / тело &f или &g так, чтобы это печатало foo вместо Any?

Я знаю два способа заставить &f использовать значение по умолчанию для $a, но ни один из них не подходит.

Опция 1:

sub f(:$a = 'foo') { say $a }
multi g(:$a) { f :$a }
multi g()    { f }
g();  # OUTPUT: «foo»

Вариант 2:

sub f(:$a = 'foo') { say $a }
sub g(:$a) { f |(:$a with $a) }
g();  # OUTPUT: «foo»

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


person codesections    schedule 09.03.2021    source источник
comment
Если я запустил ваш первый пример, я получу ошибку, а не (Any)   -  person ugexe    schedule 10.03.2021
comment
Да, g () передает позиционный аргумент в f (), не именованный аргумент. ОБНОВЛЕНИЕ: исправленный пример   -  person Elizabeth Mattijsen    schedule 10.03.2021
comment
Спасибо вам обоим. Да, это было то, что я задумал; извиняюсь за опечатку.   -  person codesections    schedule 10.03.2021
comment
Я не думаю, что это имеет значение, потому что я думаю, что Лиз это удалось - по крайней мере, ее ответ - тот, который я собирался написать, - но первый пример все еще не работает, а вместо этого говорит Cannot unbox a type object (Any) to a str.   -  person raiph    schedule 10.03.2021
comment
Спасибо, @raiph - ясно, что я не должен спешить с вопросом, прежде чем уйти в афк! Я удалил ограничения родного типа   -  person codesections    schedule 10.03.2021
comment
Никакого неуважения к тем, кто опубликовал ответ, но я считаю вариант 2 sub g(:$a) { f |(:$a with $a) } очень удобочитаемым. Ниже я обнаружил, что недооцениваю, когда читаю g a => "bar"; g;. Без обид, @ElizabethMattijsen!   -  person jubilatious1    schedule 10.03.2021
comment
@ jubilatious1 не используется :-) Проблема с вариантом 2 заключается в том, что он излишне актуализирует $_, |(:$a if $a.defined) не стал бы :-)   -  person Elizabeth Mattijsen    schedule 10.03.2021


Ответы (1)


Я бы использовал либо вариант 1, либо если sub g всегда просто вызывает sub f, чтобы создать захват всех параметров и просто передать его:

sub f(str :$a = 'foo') { say $a }
sub g(|c) { f |c }
g a => "bar";  # "bar"
g;             # "foo"
person Elizabeth Mattijsen    schedule 09.03.2021
comment
Передача захвата - это то, что я делал несколько раз, и это кажется ... странным, потому что, AFAICT, аргументы распаковываются в слипе и переупаковываются для вызова. Или компилятор может поймать sub foo(|c) { bar |c } для беспрепятственной передачи захвата? - person user0721090601; 10.03.2021
comment
Я всегда предполагал, что ... |c в списке параметров связывает символ c со свежим захватом, созданным из оставшейся части входящего захвата, которая еще не была удалена обработкой подписи до |c. Если |c находится прямо в начале подписи, компилятор просто привязывается к исходному захвату. Точно так же |c в списке аргументов выполняет логическое обратное, условно вставляя c захват в новый захват, создаваемый для вызова, но, опять же, предположительно просто используя захват напрямую как есть, если в списке аргументов больше ничего нет. - person raiph; 10.03.2021
comment
@raiph Это определенно то, что я надеюсь, будет (оптимальным) случаем, но я не был уверен, происходит это на самом деле или нет. Для того, что я делал, реальной альтернативы не было, поэтому я особо не задумывался об этом, это просто напомнило мне об этом. - person user0721090601; 10.03.2021
comment
Имейте в виду, что если sub g просто всегда вызывает sub f, то, возможно, my constant &g = &f; будет лучшим решением. :-) - person Elizabeth Mattijsen; 10.03.2021
comment
@ElizabethMattijsen iirc Я использовал это для подкласса хэша, для которого не вызываются BUILD и TWEAK. Итак, моим новым был method new (|c) { self.bless!build: |c }, где метод сборки в конце возвращал self. Это было немного неуклюже, но работало - person user0721090601; 10.03.2021
comment
Интересно ... хммм ... это можно считать ошибкой? - person Elizabeth Mattijsen; 10.03.2021
comment
@ElizabethMattijsen Мне нужно было найти, где я ссылаюсь на это в своей документации, но я помню, как ссылался на проблему по теме, где было отмечено, что это было задумано по какой-то причине, которую я сейчас забыл. - person user0721090601; 11.03.2021
comment
@ user0721090601 Я использовал это для подкласса хэша, для которого не вызываются BUILD и TWEAK. Звучит странно. class A { submethod TWEAK { say 42 } }; class B is A {}; B.new; # 42. - person raiph; 15.03.2021
comment
@raiph: попробуйте class A is Hash { submethod TWEAK { say 42 }; submethod BUILD { say 24 } }; A.new Это характерно для Hash (и нескольких других классов). - person user0721090601; 15.03.2021