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

1 2  View All