Рассмотрим эти подпрограммы, которые принимают один именованный параметр. Именованные параметры должны быть необязательными, и я не видел ничего сказать, что есть исключения.
Без ограничений типа нет проблем; именованный параметр не требуется. С типом ограничения, которое может принять объект типа (без аннотации, :U
и :_
), нет проблем.
Parameter '$quux' of routine 'quux' must be an object instance of type 'Int',
not a type object of type 'Int'. Did you forget a '.new'?
in sub quux at /Users/brian/Desktop/type.p6 line 16
in block <unit> at /Users/brian/Desktop/type.p6 line 37
При ограничении типа, которое требует определенного значения (аннотируется с помощью :D
), именованный параметр больше не является необязательным. То есть, с любым из других определений мне не нужно давать значение. При значении :D
я должен указать значение. Я бы предпочел не оставлять :D
, потому что значение, которое я хочу, должно быть определено.
Из Signatures docs:
Обычно ограничение типа проверяет, имеет ли значение принятое значение.
Но я не передаю никакой ценности. Я полагал, что эти ограничения будут иметь значение только для задания. Поскольку я не предоставляю явное значение для назначения, я ожидал, что не будет никакого назначения и не будет проблем. Это не так с Rakudo 2017.10. Это заставляет меня обходить это различными неприятными способами. Это связано с моим вопросом Является ли этот один аргумент или нет для блока Perl 6?, где я пытаюсь отличить нуль от одного аргумента.
Я мог бы обойти это, назначив значение по умолчанию, но в некоторых случаях значения по умолчанию не имеют смысла. А Bool
легко, например, но какой определенный Int
будет соответствовать? Как бы то ни было, это было бы волшебным значением, которое усложняло бы и отвлекало бы код. Я сделал это с помощью Имеет ли Perl 6 Infinite Int, но мне это не удается, потому что Inf
работает как допустимое значение в этом случае.
sub foo ( :$foo ) {
put $foo.defined ?? 'foo defined' !! 'foo not defined';
}
sub bar ( Int :$bar ) {
put $bar.defined ?? 'bar defined' !! 'bar not defined';
}
sub baz ( Int:U :$baz ) {
put $baz.defined ?? 'baz defined' !! 'baz not defined';
}
sub quux ( Int:D :$quux ) {
put $quux.defined ?? 'quux defined' !! 'quux not defined';
}
sub quack ( Int:_ :$quack ) {
put $quack.defined ?? 'quack defined' !! 'quack not defined';
}
foo();
foo( foo => 2 );
bar();
bar( bar => 2 );
baz();
baz( baz => Int );
quack();
quack( quack => 2 );
quux( quux => 2 );
quux();
Ответ 1
Я решил это, проверив точно тип Any
или интеллектуальное сопоставление для того, которое я действительно хочу:
sub f ( :$f where { $^a.^name eq 'Any' or $^a ~~ Int:D } ) {
put $f.defined ?? "f defined ($f)" !! 'f not defined';
}
f( f => 5 );
f();
Чтобы ответить на мой исходный вопрос: требуются все параметры. Это просто вопрос о том, как они получают ценности. Это может быть аргумент, явное умолчание или неявное значение по умолчанию (основанное на его ограничении типа).
Ответ 2
Все параметры всегда имеют некоторое значение, даже если они являются необязательными. Я уточнил документы, которые вы ссылаетесь в 379678 и b794a7.
Необязательные параметры имеют значения по умолчанию по умолчанию, которые являются объектом типа явных или неявных ограничений типа (неявное ограничение Any
для подпрограмм и Mu
для блоков).
sub (Int $a?, Num :$b) { say "\$a is ", $a; say "\$b is ", $b }()
# OUTPUT:
# $a is (Int)
# $b is (Num)
Выше, значения по умолчанию по умолчанию передают ограничение типа на параметры. И то же самое происходит, если вы используете смайлики типа :U
или :_
.
Однако, когда вы используете смайлик типа :D
, значение по умолчанию больше не соответствует типу ограничения. Если это не было отменено, вы потеряете преимущество указания ограничения :D
. Поскольку объекты типа по умолчанию по умолчанию будут, например, вызывать взрыв в теле этой процедуры, который ожидает, что параметры будут определенными значениями.
sub (Int:D $a?, Num:D :$b) { say $a/$b }()
И чтобы ответить на титульный вопрос о том, нужно ли указывать параметр :D
автоматически. Я нахожусь на этой идее, так как она вводит специальный случай, когда пользователям нужно будет просто запомнить один символ ввода (!
, чтобы пометить параметр по мере необходимости). Это также приводит к менее полезной ошибке, которая говорит о arity или требуемых параметрах, а не о реальной проблеме: по умолчанию по умолчанию для параметров, не соответствующих типу typecheck в параметре.
Ответ 3
FWIW, аналогичная проблема существует с дополнительными позиционными параметрами:
sub a(Int:D $number?) { ... }
a; # Parameter '$number' of routine 'a' must be an object instance of type 'Int', not a type object of type 'Int'. Did you forget a '.new'
Такая же проблема возникает, если вы укажете объект типа по умолчанию:
sub a(Int:D $number = Int) { ... };
a; # # Parameter '$number' of routine 'a' must be an object instance of type 'Int', not a type object of type 'Int'. Did you forget a '.new'
Я боюсь, что это просто следствие ограничения :D
при применении к параметрам: вы просто должны указать определенное значение по умолчанию для его возможности вызова без каких-либо параметров.
Другим подходом может быть, конечно, использование параметра multi sub
и требуемого именованного параметра:
multi sub a() { say "no foo named" }
multi sub a(Int:D :$foo!) { say "foo is an Int:D" }
Надеюсь, что это поможет