Mailing List Archive

Things you can't do in a signatured sub
I'm currently having a bit of hack at the docs in perlsub.pod,
adjusting the wording about @_ when talking about signatured
subroutines (ahead of merging my branch[1] to add discouragement
warnings to it). I feel it would be useful to keep in mind a concrete
list of the things that signatured subs don't let us do right now, so
we can think about future ideas:

* Setting @_ so you can do a 'goto &SUB' tailcall (or a perl4-style
call but we don't like those any more); much as you would with

sub not_signatured {
@_ = ("args", "to", "callee");
goto &elsewhere;
}

sub signatured($x) {
@_ = ... # not allowed
}

* Seeing the entire collection of arguments so you can write a simple
wrapper-function and forward on everything to a callee:

sub not_signatured {
my ($x) = @_;
warn "Calling with x=$x\n";
return otherfunc(@_);
}

sub signatured($x, @) {
warn "Calling with x=$x\n";
return otherfunc(@_); # not allowed
}

* As a variant of the above; you can't even see the count of passed
arguments in order to distinguish no-argument from being explicitly
passed undef (or whatever the param default is)

sub not_signatured {
my ($x) = @_;
warn "X was " ? (@_ ? "the value $x" : "not passed");
}

sub signatured($x = undef) {
# impossible to distinguish signatured() from signatured(undef)
}

* Assigning to or otherwise mutating the caller's arguments:

sub not_signatured {
$_[0] = uc $_[0];
}

sub signatured($x) {
$_[0] = uc $x; # not allowed
}

If we make signatures non-experimental and add the warning about @_
being forbidden, we should keep these cases in mind. At the very least
I'd like to document them, but ideally we should further investigate,
for each of them, whether we're actually happy to tell users "yeah,
don't do that" or whether we feel we need to provide some alternative
facility to allow them to continue writing functions to perform that
kind of behaviour, while using signatures.


[1] - https://github.com/Perl/perl5/pull/19346

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Wed, Jan 19, 2022 at 7:00 PM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> I'm currently having a bit of hack at the docs in perlsub.pod,
> adjusting the wording about @_ when talking about signatured
> subroutines (ahead of merging my branch[1] to add discouragement
> warnings to it). I feel it would be useful to keep in mind a concrete
> list of the things that signatured subs don't let us do right now, so
> we can think about future ideas:
>
> * Setting @_ so you can do a 'goto &SUB' tailcall (or a perl4-style
> call but we don't like those any more); much as you would with
>
> sub not_signatured {
> @_ = ("args", "to", "callee");
> goto &elsewhere;
> }
>
> sub signatured($x) {
> @_ = ... # not allowed
> }
>
> * Seeing the entire collection of arguments so you can write a simple
> wrapper-function and forward on everything to a callee:
>
> sub not_signatured {
> my ($x) = @_;
> warn "Calling with x=$x\n";
> return otherfunc(@_);
> }
>
> sub signatured($x, @) {
> warn "Calling with x=$x\n";
> return otherfunc(@_); # not allowed
> }
>
> * As a variant of the above; you can't even see the count of passed
> arguments in order to distinguish no-argument from being explicitly
> passed undef (or whatever the param default is)
>
> sub not_signatured {
> my ($x) = @_;
> warn "X was " ? (@_ ? "the value $x" : "not passed");
> }
>
> sub signatured($x = undef) {
> # impossible to distinguish signatured() from signatured(undef)
> }
>
> * Assigning to or otherwise mutating the caller's arguments:
>
> sub not_signatured {
> $_[0] = uc $_[0];
> }
>
> sub signatured($x) {
> $_[0] = uc $x; # not allowed
> }
>
> If we make signatures non-experimental and add the warning about @_
> being forbidden, we should keep these cases in mind. At the very least
> I'd like to document them, but ideally we should further investigate,
> for each of them, whether we're actually happy to tell users "yeah,
> don't do that" or whether we feel we need to provide some alternative
> facility to allow them to continue writing functions to perform that
> kind of behaviour, while using signatures.
>
>
> [1] - https://github.com/Perl/perl5/pull/19346
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>

I have one sad example to add: defining a sub that is passed as a callback
to a module which doesn't document the (number of) arguments and call it in
different parts with a different number of arguments. Example I'm just
dealing with is Mojo::RabbitMQ::Client.
In the end I gave up and didn't specify a signature to avoid exceptions
caused by the different number of arguments.

One more I can think of is different sub behavior depending on the number
of arguments, like multi-dispatch but inside the sub.
Then naming the arguments might be hard because for example the first one
holds something different when paired with a second arg.
Re: Things you can't do in a signatured sub [ In reply to ]
2022-1-20 5:43 Alexander Hartmaier <alex.hartmaier@gmail.com> wrote

>
> Example I'm just dealing with is Mojo::RabbitMQ::Client.
> In the end I gave up and didn't specify a signature to avoid exceptions
> caused by the different number of arguments.
>
>
Thank you for reporting in case you give up using signatures.
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
> I have one sad example to add: defining a sub that is passed as a callback to a
> module which doesn't document the (number of) arguments and call it in different
> parts with a different number of arguments. Example I'm just dealing with is
> Mojo::RabbitMQ::Client.
> In the end I gave up and didn't specify a signature to avoid exceptions caused
> by the different number of arguments.

So here's a question. The way signatures work now, if one puts "@" at the end
of the signature, does that say silently accept but throw away the extra
arguments, or is there some way in a signature to say explicitly that you take a
variable number of arguments and there is an array you can get "the rest" in?
And if this feature is used in one way, eg with a signature of just "(@)" it
kind of simulates the old way in that all the args are in an array.

> One more I can think of is different sub behavior depending on the number of
> arguments, like multi-dispatch but inside the sub.
> Then naming the arguments might be hard because for example the first one holds
> something different when paired with a second arg.

Aside from saying this seems like a poor design for a sub in the first place, I
would say that as long as the number of arguments is the same, you can just give
them names that indicate the options, for example "($foo, $bar_or_baz)" where
"$bar" and "$baz" are the names you would have used if they were discrete
arguments whose existence was conditional on the value of "$foo".

Similarly, I find its good to explicitly name variables or parameters to reflect
that they might be optional or might have different things at different times.
A common example is call something "$maybe_foo" rather than "$foo" if the thing
doesn't actually have a "$foo" at all times and might instead be empty. That
makes the code more self-documenting and easy to understand

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On Thu, Jan 20, 2022 at 12:20 AM Darren Duncan <darren@darrenduncan.net>
wrote:

> On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
> > I have one sad example to add: defining a sub that is passed as a
> callback to a
> > module which doesn't document the (number of) arguments and call it in
> different
> > parts with a different number of arguments. Example I'm just dealing
> with is
> > Mojo::RabbitMQ::Client.
> > In the end I gave up and didn't specify a signature to avoid exceptions
> caused
> > by the different number of arguments.
>
> So here's a question. The way signatures work now, if one puts "@" at the
> end
> of the signature, does that say silently accept but throw away the extra
> arguments, or is there some way in a signature to say explicitly that you
> take a
> variable number of arguments and there is an array you can get "the rest"
> in?


($arg, @rest)

-Dan
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-19 9:32 p.m., Dan Book wrote:
> On Thu, Jan 20, 2022 at 12:20 AM Darren Duncan wrote:
> On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
> > I have one sad example to add: defining a sub that is passed as a
> callback to a
> > module which doesn't document the (number of) arguments and call it in
> different
> > parts with a different number of arguments. Example I'm just dealing with is
> > Mojo::RabbitMQ::Client.
> > In the end I gave up and didn't specify a signature to avoid exceptions
> caused
> > by the different number of arguments.
>
> So here's a question.  The way signatures work now, if one puts "@" at the end
> of the signature, does that say silently accept but throw away the extra
> arguments, or is there some way in a signature to say explicitly that you
> take a
> variable number of arguments and there is an array you can get "the rest" in?
>
> ($arg, @rest)

Okay, so in that case, I disagree with what Alexander said. It sounds like
there is nothing you can't do with signatures that you could do before. If you
declare the signature with an array to capture miscellaneous extras, then
problem doesn't exist. Alexander, do you agree? -- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
2022-1-20 14:32 Dan Book <grinnz@gmail.com> wrote:

> On Thu, Jan 20, 2022 at 12:20 AM Darren Duncan <darren@darrenduncan.net>
> wrote:
>
>> On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
>> > I have one sad example to add: defining a sub that is passed as a
>> callback to a
>> > module which doesn't document the (number of) arguments and call it in
>> different
>> > parts with a different number of arguments. Example I'm just dealing
>> with is
>> > Mojo::RabbitMQ::Client.
>> > In the end I gave up and didn't specify a signature to avoid exceptions
>> caused
>> > by the different number of arguments.
>>
>> So here's a question. The way signatures work now, if one puts "@" at
>> the end
>> of the signature, does that say silently accept but throw away the extra
>> arguments, or is there some way in a signature to say explicitly that you
>> take a
>> variable number of arguments and there is an array you can get "the rest"
>> in?
>
>
> ($arg, @rest)
>
> -Dan
>

Do you recommend the following code to the Perl users?

sub foo ($arg, @rest) {
my ($foo, $bar) = @rest;
}

Instead of

sub foo ($arg, $foo, $bar) {

}
Re: Things you can't do in a signatured sub [ In reply to ]
2022-1-20 14:20 Darren Duncan <darren@darrenduncan.net> wrote:

>
> So here's a question. The way signatures work now, if one puts "@" at the
> end
> of the signature,
>

Would you recommend the following code to Perl users if they want the old
codes to be a signature without changing its behavior?

# Old code
sub f1 {
my ($a1, $a2) = @_;
}

sub f2 {
my ($a1, $a2) = @_;
}

# ...

sub f1000 ($a1, $a2) {
my ($a1, $a2) = @_;
}

# New codes
use feature 'signatures';

sub f1 ($a1, $a2, @) {

}

sub f2 ($a1, $a2, @) {

}

# ...

sub f1000 ($a1, $a2, @) {

}
Re: Things you can't do in a signatured sub [ In reply to ]
On Thu, Jan 20, 2022 at 2:08 AM Yuki Kimoto <kimoto.yuki@gmail.com> wrote:

>
>
> 2022-1-20 14:32 Dan Book <grinnz@gmail.com> wrote:
>
>> On Thu, Jan 20, 2022 at 12:20 AM Darren Duncan <darren@darrenduncan.net>
>> wrote:
>>
>>> On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
>>> > I have one sad example to add: defining a sub that is passed as a
>>> callback to a
>>> > module which doesn't document the (number of) arguments and call it in
>>> different
>>> > parts with a different number of arguments. Example I'm just dealing
>>> with is
>>> > Mojo::RabbitMQ::Client.
>>> > In the end I gave up and didn't specify a signature to avoid
>>> exceptions caused
>>> > by the different number of arguments.
>>>
>>> So here's a question. The way signatures work now, if one puts "@" at
>>> the end
>>> of the signature, does that say silently accept but throw away the extra
>>> arguments, or is there some way in a signature to say explicitly that
>>> you take a
>>> variable number of arguments and there is an array you can get "the
>>> rest" in?
>>
>>
>> ($arg, @rest)
>>
>> -Dan
>>
>
> Do you recommend the following code to the Perl users?
>
> sub foo ($arg, @rest) {
> my ($foo, $bar) = @rest;
> }
>
> Instead of
>
> sub foo ($arg, $foo, $bar) {
>
> }
>
>
>
For the cases specified in this thread, yes:

* if you want to allow for more arguments to be provided or added to the
API at a later date
* if you want to count the number of arguments provided
* if you want to conveniently pass the set of arguments on to another
function

-Dan
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-19 11:08 p.m., Yuki Kimoto wrote:
> 2022-1-20 14:32 Dan Book wrote:
> ($arg, @rest)
>
>
> Do you recommend the following code to the Perl users?
>
>   sub foo ($arg, @rest)  {
>     my ($foo, $bar) = @rest;
>   }
>
> Instead of
>
>   sub foo ($arg, $foo, $bar)  {
>   }

For the scenario where we want foo() to effectively have multiple alternative
parameter lists, and we are unwilling to have a different sub name for each
distinct parameter list, and Perl doesn't support overloading, then the first
option is indeed the best option that we have.

The second option is best when foo just has one parameter list to support.

In the general case, where alternatives are supported, the parameters which are
constant between all variants should be spelled out individually in the
signature, and @rest would have the parameters that vary.

In the special case of multiple alternative parameter lists where all the
alternatives have the same arity, we can instead have something like this:

sub foo ($arg, $foo_or_x, $bar_or_y)) {
}

... whatever the developer wants to do, they are given the choice.

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-19 11:14 p.m., Yuki Kimoto wrote:
> 2022-1-20 14:20 Darren Duncan wrote:
> So here's a question.  The way signatures work now, if one puts "@" at the end
> of the signature,
>
> Would you recommend the following code to Perl users if they want the old codes
> to be a signature without changing its behavior?
>   # Old code
>   sub f1 {
>     my ($a1, $a2) = @_;
>   }
>
>   # ...
>
>   sub f1000 ($a1, $a2) {
>     my ($a1, $a2) = @_;
>   }
>
>   # New codes
>   use feature 'signatures';
>
>   sub f1 ($a1, $a2, @) {
>   }
>
>   # ...
>
>   sub f1000 ($a1, $a2, @) {
>   }

Yes I would. That is the textbook example for the most common cases of a
subroutine where we want to use signatures while preserving the old behavior
including the lack of arity check.

Well except for the common idiom of a combined getter/setter, which I personally
don't like but it is common. So in that case you would have in the new version
of that like this:

sub myaccessor ($name, @maybe_new_value) {
if (scalar @maybe_new_value > 0)
$props->{$name} = $maybe_new_value[0];
}
return $props->{$name};
}

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On Thu, Jan 20, 2022 at 7:13 AM Darren Duncan <darren@darrenduncan.net>
wrote:

> On 2022-01-19 9:32 p.m., Dan Book wrote:
> > On Thu, Jan 20, 2022 at 12:20 AM Darren Duncan wrote:
> > On 2022-01-19 12:43 p.m., Alexander Hartmaier wrote:
> > > I have one sad example to add: defining a sub that is passed as a
> > callback to a
> > > module which doesn't document the (number of) arguments and call
> it in
> > different
> > > parts with a different number of arguments. Example I'm just
> dealing with is
> > > Mojo::RabbitMQ::Client.
> > > In the end I gave up and didn't specify a signature to avoid
> exceptions
> > caused
> > > by the different number of arguments.
> >
> > So here's a question. The way signatures work now, if one puts "@"
> at the end
> > of the signature, does that say silently accept but throw away the
> extra
> > arguments, or is there some way in a signature to say explicitly
> that you
> > take a
> > variable number of arguments and there is an array you can get "the
> rest" in?
> >
> > ($arg, @rest)
>
> Okay, so in that case, I disagree with what Alexander said. It sounds
> like
> there is nothing you can't do with signatures that you could do before.
> If you
> declare the signature with an array to capture miscellaneous extras, then
> problem doesn't exist. Alexander, do you agree? -- Darren Duncan
>

Seems I have missed the possibility to do that in perlsub (
https://perldoc.perl.org/perlsub#Signatures). Maybe because the very first
example which shows this special case is a complex one using lvalue and its
variable is called @c. I'd suggest to start with a simpler example and have
a dedicated one for that case which calls it @rest. (variables 'at rest' ;-)

Thanks to all your input I've now converted all subs to signatured subs
adding @ to the sig of all undocumented methods, just to be sure to not
cause an exception.

As the docs are pretty long, sub-headings ???? would help to quicker find the
correct example for ones use case.
If there is an agreement on this I'm willing to write patch.
Re: Things you can't do in a signatured sub [ In reply to ]
On Wed, 19 Jan 2022 17:59:41 +0000, "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> * As a variant of the above; you can't even see the count of passed
> arguments in order to distinguish no-argument from being explicitly
> passed undef (or whatever the param default is)
>
> sub not_signatured {
> my ($x) = @_;
> warn "X was " ? (@_ ? "the value $x" : "not passed");
> }
>
> sub signatured ($x = undef) {
> # impossible to distinguish signatured () from signatured (undef)
> }

FWIW: this is also impossible in raku. I have requested a feature to
detect this, but it was declined then (years ago) because it was
blocking possible optimizations in "hiding" the argument hash.

With signatures and named arguments, this is much more complicated than
the example you give:

method foo (Int :$x, Int :$y = 42, Bool :$z = False) {
}

foo (x => 1, z => False);
foo (y => 42, :$z);

You cannot use arity to check *what* named argument was passed and if
it was assigned a default value or if the caller passed the default
value.

--
H.Merijn Brand https://tux.nl Perl Monger http://amsterdam.pm.org/
using perl5.00307 .. 5.33 porting perl5 on HP-UX, AIX, and Linux
https://tux.nl/email.html http://qa.perl.org https://www.test-smoke.org
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-20 3:35 a.m., H.Merijn Brand wrote:
> With signatures and named arguments, this is much more complicated than
> the example you give:
>
> method foo (Int :$x, Int :$y = 42, Bool :$z = False) {
> }
>
> foo (x => 1, z => False);
> foo (y => 42, :$z);
>
> You cannot use arity to check *what* named argument was passed and if
> it was assigned a default value or if the caller passed the default
> value.

And now we're on a tangent that was also important to me, which is that I feel
support for native named parameters is important, and while current signatures
doesn't have them.

It would be great if what becomes non-experimental with signatures now is
something that doesn't have design decisions that would block the later addition
of named parameters.

Named parameters are generally the most elegant way to support what would
otherwise be alternative sets of positional parameters or optional parameters,
and they are generally the most elegant way to design constructors or
multi-property-setters.

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On Thu, Jan 20, 2022 at 4:20 AM Alexander Hartmaier <
alex.hartmaier@gmail.com> wrote:

>
> Seems I have missed the possibility to do that in perlsub (
> https://perldoc.perl.org/perlsub#Signatures). Maybe because the very
> first example which shows this special case is a complex one using lvalue
> and its variable is called @c. I'd suggest to start with a simpler example
> and have a dedicated one for that case which calls it @rest. (variables 'at
> rest' ;-)
>
> Thanks to all your input I've now converted all subs to signatured subs
> adding @ to the sig of all undocumented methods, just to be sure to not
> cause an exception.
>
> As the docs are pretty long, sub-headings ???? would help to quicker find
> the correct example for ones use case.
> If there is an agreement on this I'm willing to write patch.
>

My USD 0.02: I'd write the patch and submit it. Waiting for "permission"
could take forever, but putting a concrete patch in front of people will
get their attention.

--
Matthew O. Persico
Re: Things you can't do in a signatured sub [ In reply to ]
2022-1-20 16:37 Darren Duncan <darren@darrenduncan.net> wrote:

>
> Well except for the common idiom of a combined getter/setter, which I
> personally
> don't like but it is common. So in that case you would have in the new
> version
> of that like this:
>
> sub myaccessor ($name, @maybe_new_value) {
> if (scalar @maybe_new_value > 0)
> $props->{$name} = $maybe_new_value[0];
> }
> return $props->{$name};
> }
>
> -- Darren Duncan
>

You may assume the current accessor will be rewritten to the following code
using signatures.

[Before]
sub force {
my $self = shift;
if (@_ > 1) {
$self->{force} = $_[0];
return $self;
}
else {
return $self->{force};
}
}

[After]
sub force ($self, $name, @value) {
if (@value > 0) {
$self->{force} = $value[0];
return $self;
}
else {
return $self->{force};
}
}

This is not so bad.
Re: Things you can't do in a signatured sub [ In reply to ]
On Fri, Jan 21, 2022 at 4:07 AM Matthew Persico <matthew.persico@gmail.com>
wrote:

>
>
> On Thu, Jan 20, 2022 at 4:20 AM Alexander Hartmaier <
> alex.hartmaier@gmail.com> wrote:
>
>>
>> Seems I have missed the possibility to do that in perlsub (
>> https://perldoc.perl.org/perlsub#Signatures). Maybe because the very
>> first example which shows this special case is a complex one using lvalue
>> and its variable is called @c. I'd suggest to start with a simpler example
>> and have a dedicated one for that case which calls it @rest. (variables 'at
>> rest' ;-)
>>
>> Thanks to all your input I've now converted all subs to signatured subs
>> adding @ to the sig of all undocumented methods, just to be sure to not
>> cause an exception.
>>
>> As the docs are pretty long, sub-headings ???? would help to quicker find
>> the correct example for ones use case.
>> If there is an agreement on this I'm willing to write patch.
>>
>
> My USD 0.02: I'd write the patch and submit it. Waiting for "permission"
> could take forever, but putting a concrete patch in front of people will
> get their attention.
>

I'm asking first to not waste my time as this happened before.
Re: Things you can't do in a signatured sub [ In reply to ]
2022-1-20 16:15 Dan Book <grinnz@gmail.com> wrote:

For the cases specified in this thread, yes:
>
> * if you want to allow for more arguments to be provided or added to the
> API at a later date
> * if you want to count the number of arguments provided
> * if you want to conveniently pass the set of arguments on to another
> function
>
> -Dan
>

If we want the number of arguments, we can make the array arguments with
signatures.

[Before]
sub foo {
my $first = shift;
if (@_ > 0) {
# ...
}
}

[After]
sub foo ($first, @rest) {
if (@rest > 0) {
# ...
}
}

This is not so bad.
Re: Things you can't do in a signatured sub [ In reply to ]
On Wed, Jan 19, 2022 at 05:59:41PM +0000, Paul "LeoNerd" Evans wrote:
> I'm currently having a bit of hack at the docs in perlsub.pod,
> adjusting the wording about @_ when talking about signatured
> subroutines (ahead of merging my branch[1] to add discouragement
> warnings to it). I feel it would be useful to keep in mind a concrete
> list of the things that signatured subs don't let us do right now, so
> we can think about future ideas:

First off, my general comment is that I made proposals for most of of this
stuff back in 2019:

http://nntp.perl.org/group/perl.perl5.porters/256677

As per individual cases:

> * Setting @_ so you can do a 'goto &SUB' tailcall (or a perl4-style
> call but we don't like those any more); much as you would with
>
> sub not_signatured {
> @_ = ("args", "to", "callee");
> goto &elsewhere;
> }
>
> sub signatured($x) {
> @_ = ... # not allowed
> }

you just do:

sub signatured($x) {
no warnings 'whatever';
local @_ = ...;
goto &elsewhere;
}

although arguably we might want to consider an improved goto mechanism
going forward; e.g.

goto &f,1,2,3; # equivalent to @_ = (1,2,3); goto &f

although I'm not sure how we would handle calling with zero args.

>
> * Seeing the entire collection of arguments so you can write a simple
> wrapper-function and forward on everything to a callee:
>
> sub not_signatured {
> my ($x) = @_;
> warn "Calling with x=$x\n";
> return otherfunc(@_);
> }
>
> sub signatured($x, @) {
> warn "Calling with x=$x\n";
> return otherfunc(@_); # not allowed
> }

With my 'query args' proposal, the ?@a parameter syntax slurps all
remaining args into @a without actually consuming them, so subsequent
parameters can still be processed. This can be combined with the '*'
aliasing proposal to get the original args rather than copies (in the same
way that @_ contains aliases to each arg). So:

- as a fallback, @args behaves exactly like @_ here:

sub foo(?*@args) {...}

- for passing all args except self to a super method:

sub foo($self, ?@point, $x=0, $y=0, $z=0) {
$self->SUPER(@point); # @point contains between 0 and 3 elements
}

> * As a variant of the above; you can't even see the count of passed
> arguments in order to distinguish no-argument from being explicitly
> passed undef (or whatever the param default is)
>
> sub not_signatured {
> my ($x) = @_;
> warn "X was " ? (@_ ? "the value $x" : "not passed");
> }
>
> sub signatured($x = undef) {
> # impossible to distinguish signatured() from signatured(undef)

Again, query args to the rescue: ??$foo is set to true if there are still
args to consume at that point:

sub foo (??$seen_x, $x) { warn "not passed" unless $seen_x; ... }

> * Assigning to or otherwise mutating the caller's arguments:
>
> sub not_signatured {
> $_[0] = uc $_[0];
> }
>
> sub signatured($x) {
> $_[0] = uc $x; # not allowed
> }

As mentioned above, my proposals include aliasing and auto-dereferencing:

sub foo (*$x) { $x++ } # like $_[0]++

sub pushelem(\@a, $elem) { push @a, $elem }
$ar = [1,2,3];
pushelem($ar, 4); # $ar now [1,2,3,4]

> If we make signatures non-experimental and add the warning about @_
> being forbidden, we should keep these cases in mind. At the very least
> I'd like to document them, but ideally we should further investigate,
> for each of them, whether we're actually happy to tell users "yeah,
> don't do that" or whether we feel we need to provide some alternative
> facility to allow them to continue writing functions to perform that
> kind of behaviour, while using signatures.

In my ideal world I would add all these features before changing the
behaviour of @_, but the consensus is that we need to remove
experimentality ASAP to maintain credibility.

So:
for 5.36, add warning;
for 5.38, remove @_ populating, and if we're lucky, stuff like aliasing
will also be in 5.38 - otherwise we tell people to revert to non-signature
subs for now for the hard stuff.

Personally I would still much prefer to add warnings and disable @_ on the
same release: that way there's not a release or two where people get
warnings but everything still seems to work; so they add 'no warnings foo'
and everything continues being fine, then all their code breaks a release
later.

--
"You're so sadly neglected, and often ignored.
A poor second to Belgium, When going abroad."
-- Monty Python, "Finland"
Re: Things you can't do in a signatured sub [ In reply to ]
On Wednesday, 19 January 2022, 19:00:12 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:

>      sub signatured($x = undef) {
>        # impossible to distinguish signatured() from signatured(undef)
>      }

I knew my 3VL discussion went over like a lead balloon, but I couldn't resist being cheeky ...

    use Unknown::Values;

    sub foo ($bar = unknown) {
        say is_unknown $bar ? "unknown" : "known";
    }

    foo();

That prints "unknown", so if someone passed in `undef`, it will print "known" because someone explicitly assigned to it.

Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
On Friday, 21 January 2022, 17:49:47 CET, Ovid via perl5-porters <perl5-porters@perl.org> wrote:

> I knew my 3VL discussion went over like a lead balloon, but I couldn't resist being cheeky ...
>
>   use Unknown::Values;
>
>   sub foo ($bar = unknown) {
>        say is_unknown $bar ? "unknown" : "known";
>    }
>
>   foo();
>
> That prints "unknown", so if someone passed in `undef`, it will print "known" because
> someone explicitly assigned to it.

Er ... because someone "didn't" explicitly assign to it. (Damned typos (sic))

Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
On Fri, 21 Jan 2022 16:49:22 +0000 (UTC)
Ovid <curtis_ovid_poe@yahoo.com> wrote:

> I knew my 3VL discussion went over like a lead balloon, but I
> couldn't resist being cheeky ...
>
>     use Unknown::Values;
>
>     sub foo ($bar = unknown) {
>         say is_unknown $bar ? "unknown" : "known";
>     }
>
>     foo();
>
> That prints "unknown", so if someone passed in `undef`, it will print
> "known" because someone explicitly assigned to it.

Wellsure but now you've just moved the goalpost - how to distinguish

foo(unknown);
foo();

Ultimately, this comes down to the fact that since it is trivially easy
in perl to distinguish the values left in the @x array in:

my @x = (undef); # or (unknown);
my @x = ();

The same easy distinction ought to be possible with subroutine
arguments. It always has been so far.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Friday, 21 January 2022, 18:11:27 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:


> Wellsure but now you've just moved the goalpost - how to distinguish
>
>  foo(unknown);
>  foo();

I would suggest that if someone passed in `unknown` without knowing what they were passing in and why, that would be a bug :)

But I also think the semantics of `foo(unknown)` are fine because a subroutine checking for unknown values would already be handling them correctly, rather than having the unexpected coercions of `undef`.

Best,
Ovid
--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-21 8:49 a.m., Ovid via perl5-porters wrote:
> On Wednesday, 19 January 2022, 19:00:12 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:
>
>>       sub signatured($x = undef) {
>>         # impossible to distinguish signatured() from signatured(undef)
>>       }
>
> I knew my 3VL discussion went over like a lead balloon, but I couldn't resist being cheeky ...
>
>     use Unknown::Values;
>
>     sub foo ($bar = unknown) {
>         say is_unknown $bar ? "unknown" : "known";
>     }
>
>     foo();
>
> That prints "unknown", so if someone passed in `undef`, it will print "known" because someone explicitly assigned to it.

Again I repeat, it is VERY easy with normal signatures to distinguish missing
arguments from explicitly undef arguments:

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
}
}

Also your Unknown feature is not the panacea you seen think it is.

How would your second example distinguish someone explicitly passing the unknown
value as an argument from someone who didn't pass an argument?

The answer is that it can't, so Unknown adds nothing as far as a solution to the
problem, your example is exactly the same as the original but more complicated.

In principle checks against a particular default value never work universally.

The ONLY solution that works universally is distinguishing that a collection
element exists or doesn't exist. What Perl exists() gives us on a hash, or
arity checks on a collection etc.

So knowing if an argument was passed only works with the method I provided.

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
Paul "LeoNerd" Evans skribis 2022-01-19 17:59 (+0000):
> * As a variant of the above; you can't even see the count of passed
> arguments in order to distinguish no-argument from being explicitly
> passed undef (or whatever the param default is)
> sub signatured($x = undef) {
> # impossible to distinguish signatured() from signatured(undef)
> }

A simple workaround is possible by introducing a new undef-like value that has this very specific purpose:

use Scalar::Util qw(refaddr);
use constant NONE => \do { my $x };
sub _provided($x) { ref($x) && refaddr($x) == refaddr(NONE) }

sub bar($self, $new = NONE) {
$self->{bar} = $new if _provided($new);
$self->{bar};
}

As checking arity for combined getter/setter methods is incredibly common, maybe it would make sense to have a very tiny module, maybe even core, that exports these, or to add them to Scalar::Util.

Of course, this depends on not using the constant for other purposes. I think that's mostly a matter of documenting that the behavior of any other use of the constant is undefined.
--
Met vriendelijke groet, // Kind regards, // Korajn salutojn,

Juerd Waalboer <juerd@tnx.nl>
TNX
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-23 12:16 p.m., Juerd Waalboer wrote:
> Paul "LeoNerd" Evans skribis 2022-01-19 17:59 (+0000):
>>  * As a variant of the above; you can't even see the count of passed
>>    arguments in order to distinguish no-argument from being explicitly
>>    passed undef (or whatever the param default is)
>>      sub signatured($x = undef) {
>>        # impossible to distinguish signatured() from signatured(undef)
>>      }
>
> A simple workaround is possible by introducing a new undef-like value that has
> this very specific purpose:
>
>     use Scalar::Util qw(refaddr);
>     use constant NONE => \do { my $x };
>     sub _provided($x) { ref($x) && refaddr($x) == refaddr(NONE) }
>
>     sub bar($self, $new = NONE) {
>         $self->{bar} = $new if _provided($new);
>         $self->{bar};
>     }
>
> As checking arity for combined getter/setter methods is incredibly common, maybe
> it would make sense to have a very tiny module, maybe even core, that exports
> these, or to add them to Scalar::Util.
>
> Of course, this depends on not using the constant for other purposes. I think
> that's mostly a matter of documenting that the behavior of any other use of the
> constant is undefined.

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
Re: Things you can't do in a signatured sub [ In reply to ]
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. 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.

Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
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
Re: Things you can't do in a signatured sub [ In reply to ]
By the way Ovid, I don't know if this was your thought, but given how the timing
of various things seem to be going right now, I feel that it would be welcome to
get official signatures out now, given that Corinna is waiting until after this
Spring's Perl release.

Because I feel it would be best for relevant parts of Corinna, anything to do
with method declarations, to build on the more general sub signatures and not
invent its own features that are only for methods when they are conceptually.
orthogonal.

In particular, assuming the usefulness of and best practice to use named
arguments for Corinna object constructors or cloning methods or multi-setter
methods etc, I feel that this shouldn't be added for methods without
simultaneously or previously adding it for regular subs.

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On Fri, 21 Jan 2022 12:25:16 +0000
Dave Mitchell <davem@iabyn.com> wrote:

> So:
> for 5.36, add warning;
> for 5.38, remove @_ populating, and if we're lucky, stuff like
> aliasing will also be in 5.38 - otherwise we tell people to revert to
> non-signature subs for now for the hard stuff.
>
> Personally I would still much prefer to add warnings and disable @_
> on the same release: that way there's not a release or two where
> people get warnings but everything still seems to work; so they add
> 'no warnings foo' and everything continues being fine, then all their
> code breaks a release later.

To this end, I have been working on a branch to print those warnings:

https://github.com/Perl/perl5/pull/19346

It sounds like we're in agreement on that part at least. Perhaps you
could give that one a review and at least we can get that part ticked
off.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Mon, 24 Jan 2022 07:01:46 +0000 (UTC)
Ovid via perl5-porters <perl5-porters@perl.org> wrote:

> 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};
>         }
>     }

I've often suggested it as `argc` or variations thereon:

On Tue, 7 Dec 2021 18:02:23 +0000
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> In these situations I think it could be quite easy to
> permit a `builtin::argc` to allow it:
>
> use builtin 'argc';
>
> sub red($self, $new = undef) {
> $self->{red} = $new if argc > 1;
> return $self->{red};
> }

https://www.nntp.perl.org/group/perl.perl5.porters/2021/12/msg262142.html

Dave M has rather strongly objected to it:

On Thu, 9 Dec 2021 15:01:21 +0000
Dave Mitchell <davem@iabyn.com> wrote:

> using the arg count as a way of determining whether a default arg
> was passed etc, is awkward, ugly and error-prone for all but the
> simplest subs:
>
> sub foo ($self, $base, $action, $flags, $result, $x, $y = 0, $z =
> 0) { my @point = ($x);
> push @point, $y if @_ > 6;
> push @point, $z if @_ > 7;
> $self->do_point(@point);
> ....
> }
>
> whereas the query params proposal allows:
>
>
> sub foo ($self, $base, $action, $flags, $result,
> ?@point,
> $x, $y = 0, $z = 0)
> {
> $self->do_point(@point);
> ....
> }

https://www.nntp.perl.org/group/perl.perl5.porters/2021/12/msg262156.html

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Monday, 24 January 2022, 15:59:55 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:


> Ovid via perl5-porters <perl5-porters@perl.org> wrote:
> > 
> > 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};
> >         }
> >     }

> I've often suggested it as `argc` or variations thereon:
<snip>
>
> https://www.nntp.perl.org/group/perl.perl5.porters/2021/12/msg262142.html
>
> Dave M has rather strongly objected to it:

As an aside, but something we might want to keep in mind for future work, many languages simply don't have this issue because of method overloading:

    public void someMethod ( int number )  {
        // do something  
    }

    public void someMethod ( int number, String name ) {
        // do something else
    }

    public void someMethod ( int Number, SomeClass obj ) {
        // do something else
    }

It's clean, easy to read, and handles dispatching nicely. Of course, ambiguities can arise and in the Java examples above, dispatching is based on the number of arguments *and* the type of arguments. 

Allowing this in Perl would be a win because, IMHO, it's easier to read and matches existing, well-known conventions in other languages:

    method name () { return $name }
    # or method name :multi () { return $name }
    method name ( $new_name ) { $name = $new_name }
    # or method name :multi ( $new_name ) { $name = $new_name }

No, I'm not suggesting we do that, but this is (to me) much easier to follow. If types get incorporated at some point in the future, I have a ton of code that could be simplified by dispatching on type rather than if/else blocks. Overloading methods/subs allows us to have logically separated blocks of code for logically separate things.

Again, this is not a suggestion or a recommendation, but something to keep in mind. I'm happy to see signatures get out the door in their current state.

Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
On Mon, 24 Jan 2022 16:54:16 +0000 (UTC)
Ovid <curtis_ovid_poe@yahoo.com> wrote:

> As an aside, but something we might want to keep in mind for future
> work, many languages simply don't have this issue because of method
> overloading:
...
> Allowing this in Perl would be a win because, IMHO, it's easier to
> read and matches existing, well-known conventions in other languages:
>
>     method name () { return $name }
>     # or method name :multi () { return $name }
>     method name ( $new_name ) { $name = $new_name }
>     # or method name :multi ( $new_name ) { $name = $new_name }

You mean like

use v5.26;
use Syntax::Keyword::MultiSub;
use experimental 'signatures';

multi sub max() { return undef; }
multi sub max($x) { return $x; }
multi sub max($x, @more) { my $y = max(@more);
return $x > $y ? $x : $y; }

-- https://metacpan.org/pod/Syntax::Keyword::MultiSub

Doesn't yet support `multi method ...` but I keep meaning to get around
to starting implementing that.

> No, I'm not suggesting we do that,

Oh, I am ;)

> but this is (to me) much easier to
> follow. If types get incorporated at some point in the future, I have
> a ton of code that could be simplified by dispatching on type rather
> than if/else blocks. Overloading methods/subs allows us to have
> logically separated blocks of code for logically separate things.
>
> Again, this is not a suggestion or a recommendation, but something to
> keep in mind. I'm happy to see signatures get out the door in their
> current state.

Yup - that's part of my grand design for 2025 - obligatory link to

https://archive.fosdem.org/2021/schedule/event/perl_in_2025/

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Monday, 24 January 2022, 20:22:52 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:

> You mean like
>
>   use v5.26;
>   use Syntax::Keyword::MultiSub;
>   use experimental 'signatures';

>   multi sub max()          { return undef; }
>   multi sub max($x)        { return $x; }
>   multi sub max($x, @more) { my $y = max(@more);
>                             return $x > $y ? $x : $y; }

>   -- https://metacpan.org/pod/Syntax::Keyword::MultiSub

> Doesn't yet support `multi method ...` but I keep meaning to get around
> to starting implementing that.

> > No, I'm not suggesting we do that,

> Oh, I am ;)

What do we do with this?

    sub max :multi(@args) {...}
    sub max :multi(%args) {...}

Because arrays and hashes flatten into a list, unless @args has an odd number of elements, we can't disambiguate them, can we?

Java also has ambiguous cases which fail, but always at compile-time. I don't think we can do that, but I would be delighted to be wrong.

Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
On Mon, 24 Jan 2022 20:09:24 +0000 (UTC)
Ovid <curtis_ovid_poe@yahoo.com> wrote:

> What do we do with this?
>
>     sub max :multi(@args) {...}
>     sub max :multi(%args) {...}
>
> Because arrays and hashes flatten into a list, unless @args has an
> odd number of elements, we can't disambiguate them, can we?

$ perl -Mexperimental=signatures -MSyntax::Keyword::MultiSub
multi sub max (@args) {...}
multi sub max (%args) {...}
Already have a slurpy function body for multi sub max at - line 2.


Compiletime error.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
> On Monday, 24 January 2022, 23:32:04 CET, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:


> $ perl -Mexperimental=signatures -MSyntax::Keyword::MultiSub
> multi sub max (@args) {...}
> multi sub max (%args) {...}
> Already have a slurpy function body for multi sub max at - line 2.

> Compiletime error.

I'm sold! (though I'd prefer a modifier instead of a keyword)


Best,
Ovid
-- 
IT consulting, training, specializing in Perl, databases, and agile development
http://www.allaroundtheworld.fr/. 

Buy my book! - http://bit.ly/beginning_perl
Re: Things you can't do in a signatured sub [ In reply to ]
On Tue, 25 Jan 2022 07:01:04 +0000 (UTC)
Ovid via perl5-porters <perl5-porters@perl.org> wrote:

> > On Monday, 24 January 2022, 23:32:04 CET, Paul "LeoNerd" Evans
> > <leonerd@leonerd.org.uk> wrote:
> > 
> > 
> > $ perl -Mexperimental=signatures -MSyntax::Keyword::MultiSub
> > multi sub max (@args) {...}
> > multi sub max (%args) {...}
> > Already have a slurpy function body for multi sub max at - line 2.
> > 
> > Compiletime error.
>
> I'm sold! (though I'd prefer a modifier instead of a keyword)

Perhaps so, though I don't think a CPAN module would be able to provide
it in that syntax, which is why I didn't try. Perhaps a future thought
of a core implementation would indeed write it `sub NAME :multi ...`

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Things you can't do in a signatured sub [ In reply to ]
On Mon, Jan 24, 2022 at 07:01:46AM +0000, Ovid via perl5-porters wrote:
> 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. 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.

Again, with query parameters this is simple:

? ? sub name ($self, ??$has_value, $value = undef) {
? ? ? ? if ($has_value) {
? ? ? ? ? ? $self->{name} = $value; # even if undef
? ? ? ? }
? ? ? ? else {
? ? ? ? ? ? return $self->{name};
? ? ? ? }
? ? }

$has_value is a boolean value which is true if there are any args
remaining at that point in parameter processing.



--
Dave's first rule of Opera:
If something needs saying, say it: don't warble it.
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-31 1:08 p.m., Dave Mitchell wrote:
> Again, with query parameters this is simple:
>
>     sub name ($self, ??$has_value, $value = undef) {
>         if ($has_value) {
>             $self->{name} = $value; # even if undef
>         }
>         else {
>             return $self->{name};
>         }
>     }
>
> $has_value is a boolean value which is true if there are any args
> remaining at that point in parameter processing.

Thanks for the explanation.

I kept hearing references to "query parameters" and they didn't really make
sense to me before in this context.

The only other place I hear "query parameters" is in reference to calling SQL
DBMSs from a program, and "query parameters" mean the same thing as plain old
"parameters", for example when one writes something like:

SELECT foo FROM bar WHERE baz = :quux

... and after you prepare that, each execution binds "quux" to an argument,
because it is a "query parameter".

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On 1/24/22 07:50, Darren Duncan 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


I understand what you are saying, and agree to a certain extent, but:


1) If a value is documented to mean 'no-parameter', and only that, if
you explicitly pass that, you get what you deserve. This is different
from using the  generic 'unknown' proposal for this, where unknown can
very well be used as legitimately as a meaningful parameter value.


2) You can always do this in your project, if there is no generic
'no-parameter' value. It's your project/library, you are free to define
such a value and use it. In fact, I think this is what we can suggest
right now as a good technique to solve this problem. Again, if someone
maliciously passes such a value explicitly, they get what they deserve.


I think this is greatly preferable to telling people to use a slurpy
parameter. You just reintroduced @_, as far as my programmer experience
is concerned. I really, really hate that. (Note that I hate the use of
ref_addr too, but that can probably be hidden somewhat.)


Bottom line, I think, if we want to depricate @_ in signatured subs --
which we probably want, as this is the only realistic opportunity to do
so -- there are two techniques (no-parameter and slurpy last param)
which make determining arity possible, while we can think of an even
better way later (soonish!).


HTH,

M4
Re: Things you can't do in a signatured sub [ In reply to ]
On Mon, Jan 31, 2022 at 02:00:41PM -0800, Darren Duncan wrote:
> I kept hearing references to "query parameters" and they didn't really make
> sense to me before in this context.
>
> The only other place I hear "query parameters" is ...

It's just a name I made up as part of a proposal.

See this thread on p5p from Nov 2019:

Subject: Jumbo Signatures extensions discussion
http://nntp.perl.org/group/perl.perl5.porters/256677

and in particular this sub-thread:

Subject: Query Parameters
http://nntp.perl.org/group/perl.perl5.porters/256680


--
If life gives you lemons, you'll probably develop a citric acid allergy.
Re: Things you can't do in a signatured sub [ In reply to ]
On 2022-01-31 2:04 p.m., Martijn Lievaart wrote:
> On 1/24/22 07:50, Darren Duncan 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
>
> I understand what you are saying, and agree to a certain extent, but:
>
> 1) If a value is documented to mean 'no-parameter', and only that, if you
> explicitly pass that, you get what you deserve. This is different from using
> the  generic 'unknown' proposal for this, where unknown can very well be used as
> legitimately as a meaningful parameter value.
<snip>

A problem with this line of reasoning is it prevents the creation of whole
classes of generic routines/operators, ones that are supposed to be
type-agnostic, because it is treating certain things as exceptions.

Would you be happy if Perl didn't have the hash "exists" operator and the only
way to tell if an element was present was to try and fetch it and test the
result for "undef"? A lot of people do it, but its a terrible solution.

I agree that slurpy "@" parameters are not ideal, but having sentinel values as
the only way to know if an argument was passed is worse.

However, there are yet other options.

Dave Mitchell's so-called "query parameters", where each optional parameter is
expressed with 2 names where one of the names is a boolean saying if the
parameter was actually given, would appear to be a good solution.

In any event, a lot of this discussion is effectively on best practices.

Practically speaking it would be best for Perl itself for signatures to support
multiple ways of doing things, supporting non-slurpy with default values plus
slurpy, and each routine writer can use the style they want based on what they
actually need. People can choose to use sentinels, but those who really need to
know about a lack of an argument without using a sentinel are also able to do so.

-- Darren Duncan
Re: Things you can't do in a signatured sub [ In reply to ]
On 1/31/22 23:19, Darren Duncan wrote:
> On 2022-01-31 2:04 p.m., Martijn Lievaart wrote:
>> I understand what you are saying, and agree to a certain extent, but:
>>
>> 1) If a value is documented to mean 'no-parameter', and only that, if
>> you explicitly pass that, you get what you deserve. This is different
>> from using the  generic 'unknown' proposal for this, where unknown
>> can very well be used as legitimately as a meaningful parameter value.
> <snip>
>
> A problem with this line of reasoning is it prevents the creation of
> whole classes of generic routines/operators, ones that are supposed to
> be type-agnostic, because it is treating certain things as exceptions.


You are creating a problem that does not exist, I think. Your argument
would hold water if I advocated to use unknown or undef or any other
value that can legitimately be passed as a parameter for this, which I
don't.


If it doesn't make sense to pass no-parameter to a function, do not do
so. Its presence has no other use than signaling there is no parameter,
so it should never be used as a legitimate value. Doctor, it hurts when
I press here.


Because it should never be passed, and only is meaningful as a default,
it can only be passed to a function as a result of a bug by the writer
of the signatured sub that uses it. And bugs are bugs, not features.


> Would you be happy if Perl didn't have the hash "exists" operator and
> the only way to tell if an element was present was to try and fetch it
> and test the result for "undef"?  A lot of people do it, but its a
> terrible solution.


Undef is a legitimate parameter value, that can be legitimately passed.
So this is a non-sequitor.


[...]


> Practically speaking it would be best for Perl itself for signatures
> to support multiple ways of doing things, supporting non-slurpy with
> default values plus slurpy, and each routine writer can use the style
> they want based on what they actually need.  People can choose to use
> sentinels, but those who really need to know about a lack of an
> argument without using a sentinel are also able to do so.


Agree, that was my conclusion too. So in the end it doesn't matter too
much, although I would say both should be in the documentation.


M4