问题
Consider these subroutines that all take a single named parameter. Named parameters should be optional and I haven't seen anything to say there are exceptions to that.
With no type constraints there's no problem; the named parameter is not required. With a type constraint that can accept a type object (no annotation, :U
, and :_
) there is no problem.
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
With a type constraint that requires a defined value (annotated with :D
) the named parameter is no longer optional. That is, with any of the other definitions I don't have to supply a value. With a :D
I must supply a value. I'd rather not leave out the :D
because the value I want must be defined.
From the Signatures docs:
Normally, a type constraint only checks whether the value passed is of the correct type.
But, I pass no value. I figured that these constraints would only matter for an assignment. Since I'm not explicitly supplying a value to assign I expected there to be no assignment and no problem. That's not the case with Rakudo 2017.10. This leads me to workaround this in various unsavory ways. This is related to my question Is that one argument or none for a Perl 6 block? where I try to distinguish between zero and one argument cases.
I could work around this by assigning a default value, but in some cases there are no default values that make sense. A Bool
is easy, for example, but what definite Int
would fit? Whatever it is would be some magical value that would complicate and distract the code. I've done this with Does Perl 6 have an Infinite Int but I get away with that because Inf
works as a valid value in that case.
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:
All parameters always have some value, even if they're optional. I clarified the docs you reference in 379678 and b794a7.
Optional parameters have default default values that are the type object of the explicit or implicit type constraints (implicit constraint is Any
for for routines and Mu
for blocks).
sub (Int $a?, Num :$b) { say "\$a is ", $a; say "\$b is ", $b }()
# OUTPUT:
# $a is (Int)
# $b is (Num)
Above, the default default values pass the type constraint on the parameters. And the same is the case if you use the :U
or :_
type smileys.
However, when you use the :D
type smiley, the default default no longer matches the type constraint. If that was left unchecked, you'd lose the benefit of specifying the :D
constraint. As the default default type objects would, for example, cause an explosion in the body of this routine, that expects the parameters to be definite values.
sub (Int:D $a?, Num:D :$b) { say $a/$b }()
And to answer the titular question of whether specifying the :D
should make parameters required automatically. I'm -1 on the idea, as it introduces a special case users will have to learn just to save a single character of typing (the !
to mark param as required). Doing so also produces a less-helpful error that talks about arity or required parameters, rather than the actual problem: the default default of the parameters failing the typecheck on the parameter.
回答2:
FWIW, a similar issue exists with optional positional parameters:
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'
The same problem occurs if you specify a type object as a default:
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'
I'm afraid that's just a consequence of the :D
constraint when applied to parameters: you just must specify a defined default value for it to be callable without any parameters.
Another approach could of course be the use of a multi sub
and required named parameter:
multi sub a() { say "no foo named" }
multi sub a(Int:D :$foo!) { say "foo is an Int:D" }
Hope this helps
回答3:
I solved this by checking for exactly the type Any
or smart matching for the one I actually want:
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();
To answer my original question: all parameters are required. It's merely a matter of how they get values. It can be through an argument, an explicit default, or an implicit default (based from its type constraint).
来源:https://stackoverflow.com/questions/48166325/why-does-constraining-a-perl-6-named-parameter-to-a-definite-value-make-it-a-req