Это один аргумент или нет для блока Perl 6?
Как в Perl 6 можно определить разницу между аргументом и без аргумента в блоке без явной подписи? У меня нет для этого практического применения, но мне любопытно.
Блок без явной подписи помещает значение в $_
:
my &block := { put "The argument was $_" };
Подпись на самом деле ;; $_? is raw
, Это один необязательный аргумент. @_
переменная не определена в блоке, потому что нет явной подписи.
Там нет аргумента, где $_
будет неопределенным:
&block(); # no argument
Но есть и одна аргументация ситуации, когда $_
будет неопределенным Тип объекта всегда неопределен:
&block(Int);
Но, $_
в котором нет ничего, фактически является Any (а не, скажем, Nil). Я не могу сказать разницу между этими двумя случаями:
&block();
&block(Any);
Вот более длинный пример:
my $block := {
say "\t.perl is {$_.perl}";
if $_ ~~ Nil {
put "\tArgument is Nil"
}
elsif ! .defined and $_.^name eq 'Any' {
put "\tArgument is an Any type object"
}
elsif $_ ~~ Any {
put "\tArgument is {$_.^name} type object"
}
else {
put "\tArgument is $_";
}
};
put "No argument: "; $block();
put "Empty argument: "; $block(Empty);
put "Nil argument: "; $block(Nil);
put "Any argument: "; $block(Any);
put "Int argument: "; $block(Int);
Обратите внимание, что аргументы no аргумент и Any аргумент показывают одно и то же:
No argument:
.perl is Any
Argument is an Any type object
Empty argument:
.perl is Empty
Argument is Slip type object
Nil argument:
.perl is Nil
Argument is Nil
Any argument:
.perl is Any
Argument is an Any type object
Int argument:
.perl is Int
Argument is Int type object
3 ответа
Вот как я это решил. Я хотел бы сделать это более чистым способом, но умственные способности языка мешают, и я должен обойти это. Это работает для позиционных параметров, но есть более глубокие изменения для именованных параметров, и я не буду иметь дело с ними здесь.
У меня был другой вопрос: почему ограничение именованного параметра Perl 6 определенным значением делает его обязательным? где ответы пояснили, что на самом деле нет дополнительных параметров. Есть просто параметры, которые имеют значение по умолчанию и что существует неявное значение по умолчанию, если я не назначу его явно.
Суть моей проблемы в том, что я хочу знать, когда я дал параметру значение, а когда нет. Я даю ему значение через аргумент или явное значение по умолчанию. Неявное значение по умолчанию - это объект типа правильного типа. Это Any
если бы я не указал тип. Это неявное значение по умолчанию должно удовлетворять любому указанному мною ограничению.
Первая цель - жестко ограничить значения, которые пользователь может предоставить при вызове кода. Если неопределенное значение недопустимо, им нельзя указывать его.
Вторая цель - легко различать особые случаи в коде. Я хочу уменьшить количество специальных знаний, которые должна знать некоторая часть более глубокого кода.
Я могу получить третий случай (где я знаю, что не было аргумента или подходящего значения по умолчанию), явно назначив специальное значение, которое, как я знаю, не может быть чем-то другим значимым. Есть значение, которое даже более бессмысленно, чем Any
, Это Mu
, Это самые неопределенные значения из всех неопределенных значений. Any
это один из двух подтипов Mu
(другой Junction
) но вы почти никогда не увидите Mu
в конечном итоге в одном из ваших значений в нормальном коде. Неопределенные вещи в коде пользователя начинаются с Any
,
Я могу создать ограничение, которое проверяет тип, который я хочу или для Mu
и установить значение по умолчанию Mu
, Если я увижу Mu
Я знаю, что не было никаких аргументов и что это Mu
потому что мое ограничение установило это.
Так как я использую Mu
Есть некоторые вещи, которые я не могу сделать, например, использовать ===
оператор. Умное сопоставление не сработает, потому что я не хочу проверять цепочку наследования. Я могу проверить имя объекта напрямую:
my $block := ->
$i where { $^a.^name eq 'Mu' or $^a ~~ Int:D } = Mu
{
say "\t.perl is {$i.perl}";
put do given $i {
when .^name eq 'Mu' { "\tThere was no argument" }
when Nil { "\tArgument is Nil" }
when (! .defined and .^name eq 'Any') {
"\tArgument is an Any type object"
}
when .defined {
"\tArgument is defined {.^name}"
}
default { "\tArgument is {.^name}" }
}
};
put "No argument: "; $block();
put "Empty argument: "; try $block(Empty); # fails
put "Nil argument: "; try $block(Nil); # fails
put "Any type argument: "; try $block(Any); # fails
put "Int type argument: "; try $block(Int); # fails
put "Int type argument: "; $block(5);
Теперь большинство из этих вызовов терпят неудачу, потому что они не указывают правильные вещи.
Если бы это были рутины, я мог бы сделать multis для небольшого числа случаев, но в конце концов это еще более худшее решение. Если бы у меня было два параметра, мне понадобилось бы четыре мульт. С тремя такими параметрами мне понадобится шесть. Это много стандартного кода. Но блоки не рутины, так что это спорный вопрос здесь.
Насколько я знаю, единственный способ узнать количество параметров, переданных без явной подписи, это использовать @_
внутри тела, которое будет генерировать :(*@_)
подпись.
my &block := { say "Got @_.elems() parameter(s)" };
block; # Got 0 parameter(s)
block 42; # Got 1 parameter(s)
dd block.signature; # :(*@_)
Да, старый добрый @_
все еще там, если вы этого хотите:-)
{ put $_.perl }
Это похоже на это: (который не работает)
-> ;; $_? is raw = CALLERS::<$_> { put $_.perl }
Так как по умолчанию is default
за $_
за пределами блока Any
, если вы ничего не поместите в $_
прежде чем вызывать функцию, которую вы получаете Any
,
Чтобы получить что-то похожее, где вы можете заметить разницу, используйте Capture:
my &foo = -> ;; |C ($_? is raw) {
unless C.elems {
# pretend it was defined like the first Block above
CALLER::<$_> := CALLER::CALLERS::<$_>
}
my $called-with-arguments := C.elems.Bool;
if $called-with-arguments {
say 'called with arguments';
} else {
say 'not called with arguments';
}
}