Reputation: 132896
In the Perl 6 Signature docs, there's an example of an anonymous slurpy parameter:
sub one-arg (@) { }
sub slurpy (*@) { }
one-arg (5, 6, 7); # ok, same as one-arg((5, 6, 7))
slurpy (5, 6, 7); # ok
slurpy 5, 6, 7 ; # ok
There are no statements in the subroutine, mostly because the text around this is about the parameter list satisfying the signature rather than what the subroutine does with it.
I was playing with that and trying to make a subroutine that takes a list of one of more items (so, not zero items). I didn't particularly care to name them. I figured I'd still have access to the argument list in @_
even with the signature. However, you get @_
when you don't have a signature:
$ perl6
To exit type 'exit' or '^D'
> sub slurpy(*@) { say @_ }
===SORRY!=== Error while compiling:
Placeholder variable '@_' cannot override existing signature
------> sub⏏ slurpy(*@) { say @_ }
Is there another way to get the argument list, or does the anonymous parameter discard them? I see them used in the section on type constraints, but there isn't an example that uses any of the parameter values. Can I still get the argument list?
Upvotes: 3
Views: 194
Reputation: 5791
Does an anonymous parameter in a Perl 6 signature discard the value?
Yes, unless you capture an argument using some named parameter in the function's signature, it won't be available in the function's body. (PS: They're not literally discarded though, as moritz's answer shows.)
I figured I'd still have access to the argument list in @_ even with the signature.
@_
is not an alternative to using parameters - it is a parameter.
Every function has a well-defined signature that specifies its parameters, and they represent the only mechanism for the function body to get at the values the function is passed.
It's just that there are three different ways to declare a function signature:
@_
or $^foo
etc.) in the body of the function, the compiler uses that information to build the signature for you: say { $^a + @_ }.signature; # ($a, *@_)
say (sub { 42 }).signature; # ()
$_
: say { 42 }.signature; # (;; $_? is raw)
In all cases, the function ends up with an unambiguous signature at compile time. (Trying to use $^foo
in a function that already has an explicit signature, is a compile-time error.)
Is there another way to get the argument list
Make sure to capture it with a non-anonymouys parameter. If you want it to be accessible as @_
, then call it that in the explicit signature you're writing.
I was [...] trying to make a subroutine that takes a list of one of more items
You can use a sub-signature for that:
sub foo (*@_ [$, *@]) { ... };
Or alternatively a where
constraint:
sub foo (*@_ where .so) { ... };
Upvotes: 4
Reputation: 34130
To have @_
populated, the signature either has to be implicit or explicit, not both.
sub implicit { say @_ } # most like Perl 5's behaviour
sub explicit ( *@_ ) { say @_ }
sub placeholder { $^a; say @_ }
This applies to blocks as well
my &implicit = { say @_ }
my &explicit = -> *@_ { say @_ }
my &placeholder = { $^a, say @_ }
Blocks can also have an implicit parameter of $_
, but @_
takes precedence if it is there.
{ say $_ }(5) # 5
$_ = 4;
{ @_; say $_ }(5) # 4
It makes sense to do it this way because one programmer may think it works the way you think it does, or that it is slurpy like it would be if implicit, and another may think it gets all of the remaining arguments.
sub identical ( @_ ) { say @_ }
sub slurpy ( *@_ ( @, *@ ) ) { say @_ } # same as implicit
sub slurpy2 ( **@_ ( @, *@ ) ) { say @_ } # non-flattening
sub remaining ( @, *@_ ) { say @_ }
identical [1,2]; # [1 2]
slurpy $[1,2],3,4,5; # [[1 2] 3 4 5]
slurpy2 [1,2],3,4,5; # [[1 2] 3 4 5]
remaining [1,2],3,4,5; # [3 4 5]
@_
may also be added as a mistake, and in that case it would be preferable for it to produce an error.
There is no way to get at the raw arguments without declaring a capture parameter.
sub raw-one ( |capture ( @ ) ) { capture.perl }
sub raw-slurpy ( |capture, *@ ) { capture.perl }
raw-one [1,2]; # \([1, 2])
raw-slurpy 1,2 ; # \(1, 2)
Upvotes: 2
Reputation: 12852
The values aren't discarded; you can for example access it through nextsame:
multi access-anon(*@) is default {
say "in first candidate";
nextsame;
}
multi access-anon(*@a) {
@a;
}
say access-anon('foo')
Output:
in first candidate
[foo]
But to get to your original objective (an array with at least one element), you don't actually need to access the list; you can use a sub-signature:
sub at-least-one(@ [$, *@]) { }
at-least-one([1]); # no error
at-least-one([]); # Too few positionals passed; expected at least 1 argument but got only 0 in sub-signature
Upvotes: 4