develooper Front page | perl.perl5.porters | Postings from January 2022

Re: Things you can't do in a signatured sub

Thread Previous | Thread Next
Dave Mitchell
January 21, 2022 12:25
Re: Things you can't do in a signatured sub
Message ID:
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:

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.

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

"You're so sadly neglected, and often ignored.
A poor second to Belgium, When going abroad."
    -- Monty Python, "Finland"

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About