Front page | perl.perl5.porters |
Postings from January 2022
Re: Things you can't do in a signatured sub
Thread Previous
|
Thread Next
From:
Darren Duncan
Date:
January 24, 2022 07:19
Subject:
Re: Things you can't do in a signatured sub
Message ID:
33c23314-cadf-c162-7ddf-e6f5dea8090a@darrenduncan.net
On 2022-01-23 11:01 p.m., Ovid via perl5-porters wrote:
> On Monday, 24 January 2022, 07:50:33 CET, Darren Duncan <darren@darrenduncan.net> wrote:
>
>> I still oppose as wrong-headed the concept of using such sentinel default values
>> to try and detect when no argument is passed. The only use of such default
>> values is when you don't care to distinguish between users explicitly passing
>> that value from them not passing anything. When you actually want to
>> distinguish them not passing a value, DON'T use defaults, and instead use "@" to
>> capture the list and test the list for presence of an element. -- Darren Duncan
>
> Then how do we fix the issue? What we *want* to know is how many arguments were passed to a variadic sub. You have this as an earlier example:
>
> sub signatured(@maybe_x) {
> if (scalar @maybe_x > 0) {
> # we were given an argument, which may or may not be undef
> }
> else {
> # we were not given an argument
> }
> }
>
> You've sort of turned @_ into @maybe_x and now you can pass 20 arguments to it, killing one of the strongest features of signatures. Both your example and @_ are to attempts to determine how many arguments were passed to a sub.
My example was the simplest version; in reality the "@foo" would only be used
for the portion of the signature which is variadic, and any arguments that would
always be passed are listed first as usual, eg:
sub signatured($x, $y, @maybe_z) { ... }
Also I feel that when people are designing their subs properly, the number of
optional parameters would be very few, usually zero, and otherwise the most
common case would be exactly one parameter is optional, such as the combined
getter/setter. When you legitimately have a large number of optional arguments,
that's when named arguments should be used. Legitimately having more than 1
optional positional argument only makes sense when each additional one is only
used when all prior optional ones are also used.
Whenever parameter defaults are used, those defaults should always be a valid
value of the parameter's data type, the same thing the user could have passed in
explicitly but chose not to. So typically that is zero if its a number, or
false if its a boolean, and so on, not undef or some weird sentinel.
> Imagine if we had a $^NUM_ARGS variable (terrible name, though I think someone else suggested it):
>
> sub name ( $self, $name=undef ) {
> if ( $^NUM_ARGS == 2) {
> $self->{name} = $name; # even if undef
> }
> else {
> return $self->{name};
> }
> }
>
> By decoupling the signature from knowing how many args are passed, the solutions is much cleaner and we can't pass 20 arguments to the sub.
Now I actually like this proposal of yours a lot in principle. Having another
variable with the count that can be consulted. And if we had that then it would
be an elegant solution that means we can avoid the very problematic idea of
sentinel values and not need slurpy "@foo" to deal with the variadic.
That being said, while it would mean a change to signatures, likely the best way
to implement this is extend the signature syntax where one can declare a
non-special lexical $foo to hold the argument count if they want it.
Here's an example I'm just making up:
sub name ( $self, $name=undef, #$num_args ) { ... }
In that case, $num_args contains the count rather than declaring a parameter.
See also as I recall Raku supports special syntax for declaring the $self as
well, like this:
sub name ( $self: $name ) { ... }
So it would be a similar idea.
And we're not using up some special $^FOO when we don't really need to.
-- Darren Duncan
Thread Previous
|
Thread Next