Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Contents of @_ using subroutine signatures with default values

by davido (Cardinal)
on Jul 13, 2020 at 22:22 UTC ( [id://11119273]=perlquestion: print w/replies, xml ) Need Help??

davido has asked for the wisdom of the Perl Monks concerning the following question:

perlsub states the following:

When using a signature, the arguments are still available in the special array variable @_ , in addition to the lexical variables of the signature.

We can confirm this:

sub baz ($this, $that) { warn '@_ contains ', scalar(@_), " elements.\n"; } baz('hello', 'world') # @_ contains 2 elements.

But what happens when one of the parameters is optional?

sub foo ($this, $that = 'world') { warn '@_ contains ', scalar(@_), " elements.\n"; } foo('hello'); # @_ contains 1 elements.

Here's an example with tests:

#!/usr/bin/env perl use strict; use warnings; use feature qw(signatures); no warnings qw(experimental::signatures); use Test::More tests => 8; sub baz($this, $that) { ok defined($this), "\$this is defined and contains $this"; ok defined($that), "\$that is defined and contains $that"; is scalar(@_), 2, '@_ has two elements.'; is_deeply \@_, [$this, $that] or diag 'Parameters: ', explain { this => $this, that => $that, at_underscore => \@_ }; } sub foo ($this, $that='bar') { ok defined($this), "\$this is defined and contains $this"; is $that, 'bar', '\$that contains "bar"'; is scalar(@_), 2, '@_ has two elements.'; is_deeply \@_, [$this, $that] or diag 'Parameters: ', explain { this => $this, that => $that, at_underscore => \@_ }; } note "\n\nTesting baz()"; baz('hello', 'world'); note "\n\nTesting foo()"; foo("a");

The output is...

1..8 # # # Testing baz() ok 1 - $this is defined and contains hello ok 2 - $that is defined and contains world ok 3 - @_ has two elements. ok 4 # # # Testing foo() ok 5 - $this is defined and contains a ok 6 - \$that contains "bar" not ok 7 - @_ has two elements. # Failed test '@_ has two elements.' # at mytest2.pl line 24. # got: '1' # expected: '2' not ok 8 # Failed test at mytest2.pl line 25. # Structures begin differing at: # $got->[1] = Does not exist # $expected->[1] = 'bar' # Parameters: { # 'at_underscore' => [ # 'a' # ], # 'that' => 'bar', # 'this' => 'a' # } # Looks like you failed 2 tests of 8. shell returned 2

This has probably been discussed a lot somewhere.

The question: Is the reason that scalar(@_) does not contain aliases to optional values so that we can determine how many parameters were actually passed, and if so, should it be documented?


Dave

Replies are listed 'Best First'.
Re: Contents of @_ using subroutine signatures with default values
by dave_the_m (Monsignor) on Jul 14, 2020 at 00:23 UTC
    One reason why signatures are still marked as experimental is that in a future release or perl, @_ probably wont be populated within signatured subroutines

    Dave.

      > @_ probably wont be populated within signatured subroutines

      This desire seems to originate from the performance impact of populating @_.

      But this will break some compatibility to older code and make migration harder.

      May I suggest a compromise to scan the functions body for the use of @_ and to dynamically decide then?

      Attaching a flag to the code-ref which is evaluated by the signature code once the parser encounters @_ shouldn't be too difficult.

      I hope you all have in mind that goto &sub relies on @_.

      So does the debugger via @DB:_ for call stack traces.

      As a side note: JS has a "arguments" array analog to @_ and performance is not JS' problem.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        The simplified answer: no, it's not that simple. If @_ isn't populated, then the signature code gets its arguments directly from the stack. If @_ gets populated, then the stack and @_ can get out of sync, e.g. by code within the signature which manipulates @_. So in that case the code must get its args from @_ rather than from the stack. So there have to be two code paths for everything.

        See this p5p post for more details.

        Dave.

      > @_ probably wont be populated

      I think it is important to note that shift is also destructive, so using that as the idiomatic control of the experimental feature shows that processing it the accepted way more often than not also results in @_ not being populated.

      This doesn't apply to another idiom (that I even use some times) that are non-destructive - like, my %opts = @_ - assuming params are passed in like foo(param1 => 2, param2 => 'foo').

      FWIW I certainly hope this comes true.


      The way forward always starts with a minimal test.
Re: Contents of @_ using subroutine signatures with default values (not a bug)
by LanX (Saint) on Jul 14, 2020 at 12:16 UTC
    Hi David,

    I'm not sure if I understand your problem ...

    ... the call foo("a") is exactly doing what I expect, i.e. @_ == 1

    Anything else - like pushing an optional $that into @_ - wouldn't really be backwards compatible.

    And the number you seem to want is static, it's the sum of obligatory arguments + defaulted ones.

    (maybe you want to make a feature request to be able to introspect the signature)

    FWIW, when interested you can look into the implementation with B::Deparse ...

    # https://perlmonks.org/?node_id=11119273 use strict; use warnings; use feature qw(signatures); no warnings qw(experimental::signatures); use B::Deparse; use Test::More; sub foo ($this, $that='bar') { return scalar @_; } is( foo(1), 1, "one arg" ); is( foo(1,2), 2, "two args" ); done_testing(); warn "Show implementation:\n", B::Deparse->new()->coderef2text(\&foo);

    C:/Perl_524/bin\perl.exe -w d:/exp/pm_signatures.pl ok 1 - one arg ok 2 - two args 1..2 Show implementation: { BEGIN {${^WARNING_BITS} = "\x54\x55\x55\x55\x55\x55\x55\x55\x55\x5 +5\x55\x55\x55\x55\x54\x55\x05"} use strict; use feature 'signatures'; die sprintf("Too many arguments for subroutine at %s line %d.\n", +(caller)[1, 2]) unless @_ <= 2; die sprintf("Too few arguments for subroutine at %s line %d.\n", ( +caller)[1, 2]) unless @_ >= 1; my $this = $_[0]; my $that = @_ >= 2 ? $_[1] : 'bar'; (); return scalar @_; } at d:/exp/pm_signatures.pl line 32. Compilation finished at Tue Jul 14 14:25:41

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    UPDATE

    expanded code with tests

      a more sophisticated test, covering errors too
      # https://perlmonks.org/?node_id=11119273 use strict; use warnings; use feature qw(signatures); no warnings qw(experimental::signatures); use B::Deparse; use Test::More; sub foo ($this, $that='bar') { return scalar @_; } t_foo(0,undef,'Too few arguments'); t_foo(1,1,"One arg"); t_foo(2,2,"Two args"); t_foo(3,undef,"Too many arguments"); done_testing(); sub t_foo { my ($n_args, $exp, $desc ) = @_; my @args = ("","a".."z")[1..$n_args]; my $code = "foo(qw/@args/)"; my $got = eval $code; unless ($@) { is( $got, $exp, "<$code> \t $desc: \@_ == $got" ); } else { ok( $@ =~ $desc, "<$code> \t ERR: $desc: "); } } #diag "Show sig implementation:\n", B::Deparse->new()->coderef2text(\& +foo);

      C:/Perl_524/bin\perl.exe -w d:/exp/pm_signatures.pl ok 1 - <foo(qw//)> ERR: Too few arguments: ok 2 - <foo(qw/a/)> One arg: @_ == 1 ok 3 - <foo(qw/a b/)> Two args: @_ == 2 ok 4 - <foo(qw/a b c/)> ERR: Too many arguments: 1..4

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re: Contents of @_ using subroutine signatures with default values
by Marshall (Canon) on Jul 14, 2020 at 11:27 UTC
    My advice is to not use experimental features in production code.
    I got bit by this "smart match" thing. At the end of the day, this thing
    was "too smart" for me.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11119273]
Approved by marto
Front-paged by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2024-04-16 19:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found