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

In defining a subroutine
manual_error() { }
I've made the error of using (). The correct is :
manual_error { }
when calling it as
manual_error("hi");
and the first definition is active then the program dies with "Too many arguments for main::manual_error "
has this something to do with prototypes?

Replies are listed 'Best First'.
Re: Too many arguments for subroutine
by Corion (Patriarch) on Oct 20, 2023 at 11:44 UTC

    See perldiag.

    Perl can give you more information on its warnings and exceptions if you let it:

    #!perl use strict; use warnings; use diagnostics; sub manual_error() { }
    Too many arguments for main::manual_error at tmp.pl line 9, near ""hi" +)" Execution of tmp.pl aborted due to compilation errors (#1) (F) The function requires fewer arguments than you specified. Uncaught exception from user code: Too many arguments for main::manual_error at tmp.pl line 9, ne +ar ""hi")" Execution of tmp.pl aborted due to compilation errors.

    And yes, this has to do with specifying a prototype (or subroutine signature) and then using a different number of parameters.

Re: Too many arguments for subroutine
by chromatic (Archbishop) on Oct 20, 2023 at 16:12 UTC
Re: Too many arguments for subroutine
by eyepopslikeamosquito (Archbishop) on Oct 20, 2023 at 14:34 UTC

    has this something to do with prototypes?

    Yes. Thanks for posting, made me realise I didn't have a list of references on this topic yet.

    So I just created one: Subroutine Prototype References (short summary: Don't use subroutine prototypes)

    👁️🍾👍🦟
      short summary: Don't use subroutine prototypes

      Well...that begs the question...what are they included in the language?

      I never use prototypes because I tried to understand them, failed, and decided to remember they exist so if I ever come across a situation where they seem like they might make life easier, I can go back and try to properly learn how to use them. That situation has occurred yet!

        The one place where Prototypes are useful* is if you want to write a replacement for a built-in function that has its arguments parsed in the same way as the builtin. For example, prototype("CORE::lc") is "_", and let's say we want to write a replacement:

        use warnings; use strict; use feature 'say'; use Data::Dump 'pp'; sub mylc (_) { print "in=", pp @_; return lc $_[0] } sub mylc2 { # no prototype for comparison print "in=", pp @_; return lc $_[0] }

        The prototype does three things:

        1. There is only one argument. If lc or its hypothetical replacement didn't have a prototype, then lc "x", "y" would be the same as lc("x", "y"), but because of the prototype, the former is the equivalent of lc("x"), "y". See also Named Unary Operators.

          say "lc: out=", pp( lc "A","B" ); print "mylc: "; say " out=", pp( mylc "A","B" ); print "mylc2: "; say " out=", pp( mylc2 "A","B" ); # lc: out=("a", "B") # mylc: in="A" out=("a", "B") # mylc2: in=("A", "B") out="a"
        2. Like the '$' prototype, the argument is forced into scalar context. If lc didn't have a prototype, if you said lc(@x), its arguments @_ would be the elements of @x. But because of the prototype, the former is actually equivalent to lc(scalar(@x)), meaning it gets only one argument, the number of elements in @x.

          my @x=qw/ X Y Z /; say "lc: out=", pp( lc @x,"B" ); print "mylc: "; say " out=", pp( mylc @x,"B" ); print "mylc2: "; say " out=", pp( mylc2 @x,"B" ); # lc: out=(3, "B") # mylc: in=3 out=(3, "B") # mylc2: in=("X", "Y", "Z", "B") out="x"
        3. It automagically uses $_ when no argument is passed to the function.

          $_ = 'Z'; say "lc: out=", pp( lc,"B" ); print "mylc: "; say " out=", pp( mylc,"B" ); print "mylc2: "; say " out=", pp( mylc2,"B" ); # lc: out=("z", "B") # mylc: in="Z" out=("z", "B") # Use of uninitialized value $_[0] in lc at example.pl line 12. # mylc2: in=() out=("", "B")

        * However:

        • this usage is quite rare in practice (unless you're actually replacing a builtin, it's better to just use Perl's default @_ behavior, as this is the least surprising),
        • not every Perl built-in actually can be modeled by prototypes (e.g. print and split get special parsing),
        • they influence how Perl code is parsed (only perl can parse Perl),
        • the above effects can be quite surprising to those who don't know about them (Fun with Prototypes), and
        • people have often tried to use prototypes as a replacement for Signatures (which Perl finally has, but did not have for a very long time) and were surprised (we get questions here about it regularly),

        therefore, they are firmly in the "only use this if you know what you are doing and can explain exactly why" category, or, rephrasing that for non-experts, "Don't use subroutine prototypes"...

        Update: Switched code from oneliners to a script.

        G'day Bod,

        [Assumption of typos: first sentence (after quote) s/what are they included/why are they included/; last sentence s/situation has occurred yet/situation has not occurred yet/.]

        "Well...that begs the question...whatwhy are they included in the language?"

        The only time I use prototypes is when I want a subroutine to be inlined (and then only in rare cases). You can read about inlining in "perlsub: Constant Functions". Reproducing the test examples (a fair way down in that section):

        $ perl -MO=Deparse -e 'sub ONE { 1 } if (ONE) { print ONE if ONE }' sub ONE { 1; } if (ONE) { print ONE() if ONE; } -e syntax OK $ perl -MO=Deparse -e 'sub ONE () { 1 } if (ONE) { print ONE if ONE }' sub ONE () { 1; } do { print 1 }; -e syntax OK

        When subroutine signatures are enabled (see ++chromatic's earlier discussion; and also "perlsub: Signatures") there's a conflict with this syntax:

        $ perl -MO=Deparse -e 'use v5.36; sub ONE () { 1 } if (ONE) { print ON +E if ONE }' sub BEGIN { require v5.36; () } use warnings; use strict; no feature ':all'; use feature ':5.36'; sub ONE () { 1; } if (ONE) { print ONE() if ONE; } -e syntax OK

        You can resolve this by using the :prototype attribute (see "perlsub: Prototypes"):

        $ perl -MO=Deparse -e 'use v5.36; sub ONE :prototype() { 1 } if (ONE) +{ print ONE if ONE }' sub ONE () { 1; } sub BEGIN { require v5.36; () } use warnings; use strict; no feature ':all'; use feature ':5.36'; do { print 1 }; -e syntax OK

        But don't also try to use a signature as that will break it again:

        $ perl -MO=Deparse -e 'use v5.36; sub ONE :prototype() () { 1 } if (ON +E) { print ONE if ONE }' sub BEGIN { require v5.36; () } use warnings; use strict; no feature ':all'; use feature ':5.36'; sub ONE : prototype() () { 1; } if (ONE) { print ONE if ONE; } -e syntax OK

        Now, I did say at the outset "only in rare cases". That's because, for the most part, the constant pragma does what I want regardless of whether signatures are enabled or not.

        $ perl -MO=Deparse -e 'use constant ONE => 1; if (ONE) { print ONE if +ONE }' use constant ('ONE', 1); do { print 1 }; -e syntax OK $ perl -MO=Deparse -e 'use v5.36; use constant ONE => 1; if (ONE) { pr +int ONE if ONE }' sub BEGIN { require v5.36; () } use warnings; use strict; no feature ':all'; use feature ':5.36'; use constant ('ONE', 1); do { print 1 }; -e syntax OK

        In fact, needing an empty prototype to inline a subroutine is so rare, I can't immediately think of an example. Perhaps I'll come up with one later. :-)

        — Ken