Это один аргумент или нет для блока 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';
    }
}
Другие вопросы по тегам