What is the Perl 6 way to tell the difference between an argument and no argument in a block with no explicit signature? I don't have any practical use for this, but I'm curious.
A block with no explicit signature puts the value into $_
:
my &block := { put "The argument was $_" };
The signature is actually ;; $_? is raw
. That's one optional argument. The @_
variable isn't defined in the block because there is no explicit signature.
There's the no argument, where $_
will be undefined:
&block(); # no argument
But there's also a one argument situation where $_
will be undefined. A type object is always undefined:
&block(Int);
But, an $_
with nothing in it is actually an Any (rather than, say, Nil). I can't tell the difference between these two cases:
&block();
&block(Any);
Here's a longer example:
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);
Notice the no argument and Any argument forms show the same things:
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
As far as I know, the only way to know the number of parameters passed without an explicit signature, is to use
@_
inside the body, which will generate a:(*@_)
signature.Yeah, the good old
@_
is still there, if you want it :-)Is sort of similar to this: (which doesn't work)
Since the default
is default
for$_
outside of the block isAny
, if you don't place anything into$_
before you call the function you getAny
.To get something at all similar where you can tell the difference use a Capture :
Here's how I solved this. I'd love to do this in a cleaner way but the cleverness of the language gets in the way and I have to work around it. This works for positional parameters but there are deeper shenanigans for named parameters and I won't deal with those here.
I had another question, Why does constraining a Perl 6 named parameter to a definite value make it a required value?, where the answers clarified that there are actually no optional parameters. There are merely parameters that have a default value and that there is an implicit default value if I don't explicitly assign one.
The crux of my problem is that I want to know when I gave the parameter a value and when I didn't. I give it a value through an argument or an explicit default. An implicit default is a type object of the right type. That's
Any
if I didn't specify a type. That implicit default must satisfy any constraint I specify.The first goal is to tightly constrain the values a user can supply when they call code. If an undefined value is not valid then they shouldn't be allowed to specify one.
The second goal is to easily distinguish special cases in the code. I want to reduce the amount of special knowledge some part of the deeper code needs to know.
I can get the third case (where I know there was no argument or suitable default) by explicitly assigning a special value that I know can't be anything other meaningful thing. There's a value that's even more meaningless than
Any
. That'sMu
. It's the most undefined values of all undefined values. TheAny
is one of two subtypes ofMu
(the other isJunction
) but you should almost never see aMu
end up in one of your values in normal code. Undefined things in user code start atAny
.I can create a constraint that checks for the type I want or for
Mu
and set a default ofMu
. If I see aMu
I know there was no argument and that it'sMu
because my constraint set that.Since I'm using
Mu
there are some things I can't do, like use the===
operator. Smart matching won't work because I don't want to test the inheritance chain. I can check the object name directly:Now most of those invocations fail because they don't specify the right things.
If these were routines I could make multis for a small number of cases but that's an even worse solution in the end. If I had two parameters I'd need four multis. With three such parameters I'd need six. That's a lot of boilerplate code. But, blocks aren't routines so that's moot here.