http://www.perlmonks.org?node_id=766188

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

This post is prompted by a sub-thread of Preferred technique for named subroutine parameters?, specifically Re^2: Preferred technique for named subroutine parameters? and replies thereto, in which a question is raised about warnings and errors generated by the technique of named subroutine parameters recommended in TheDamian's Perl Best Practices. At the suggestion of LanX, I am making this a new thread.

This question pertains to the hard copy of the first English edition, printed July 2005, chapter 9, pages 182 - 183, the "Named Arguments" section. I have checked the on-line errata lists and there are no corrections or alterations for these pages.

The discussion of "named arguments" contrasts building an anonymous hash of name/parameter pairs in the argument list of a function call with directly assigning the argument list (i.e., @_) to a hash within the body of the function.

With respect to building an anonymous hash of named arguments, page 183, second paragraph, states that errors "... will be reported (usually at compile time) in the caller's context ..." (emphasis added), but I don't see that this is so. (However, everything else works as advertised: the anonymous hash version of named arguments produces a warning at the point of the function invocation.)

Certainly, a malformed function call like
    func({ one => 'uno',  two => 'dos',  three => });
from the example code below does compile and only warns at run time. Can anyone give an example of a compile time error (or warning) associated with this invocation technique, or any explanation or example of what TheDamian was referring to?

Various malformed examples, code and output:

use warnings; use strict; print "program running \n"; # these compile and run WITHOUT any warnings na_anon ({ one => 'uno', two => 'dos', => }); na_anon ({ one => 'uno', two => 'dos', , }); na_anon ({ one => 'uno', => two => 'dos' }); na_anon ({ one => 'uno', , two => 'dos' }); na_anon ({ one => 'uno', => => two => 'dos' }); na_anon ({ one => 'uno', , , two => 'dos' }); na_assign( one => 'ein', two => 'zwei', => ); na_assign( one => 'ein', two => 'zwei', , ); na_assign( one => 'ein', => two => 'zwei' ); na_assign( one => 'ein', , two => 'zwei' ); na_assign( one => 'ein', => => two => 'zwei' ); na_assign( one => 'ein', , , two => 'zwei' ); # these compile and run WITH run-time warnings na_anon ({ one => 'uno', two => 'dos', three => }); na_anon ({ one => 'uno', two => 'dos', three => 20..21 }); na_assign( one => 'ein', two => 'zwei', three => ); na_assign( one => 'ein', two => 'zwei', three => 20..21 ); print "program done \n"; sub na_anon { my %args = %{ $_[0] }; my $n = 'one'; print "anon hash: $n translates to $args{$n} \n"; } sub na_assign { my %args = @_; my $n = 'two'; print "hash assign: $n translates to $args{$n} \n"; }
Output:
>perl np_ah_vs_ha_1.pl program running anon hash: one translates to uno anon hash: one translates to uno anon hash: one translates to uno anon hash: one translates to uno anon hash: one translates to uno anon hash: one translates to uno hash assign: two translates to zwei hash assign: two translates to zwei hash assign: two translates to zwei hash assign: two translates to zwei hash assign: two translates to zwei hash assign: two translates to zwei Odd number of elements in anonymous hash at np_ah_vs_ha_1.pl line 71. anon hash: one translates to uno Odd number of elements in anonymous hash at np_ah_vs_ha_1.pl line 72. anon hash: one translates to uno Odd number of elements in hash assignment at np_ah_vs_ha_1.pl line 85. hash assign: two translates to zwei Odd number of elements in hash assignment at np_ah_vs_ha_1.pl line 85. hash assign: two translates to zwei program done

Update: Slight clarification in wording of code comments.

Replies are listed 'Best First'.
Re: Named Subroutine Parameters: Compile-time errors vs. Run-time warnings
by shmem (Chancellor) on May 26, 2009 at 12:23 UTC
    Can anyone give an example of a compile time error (or warning) associated with this invocation technique

    No, anonymous hash population is a run-time thing. The compiler doesn't count the elements used for the assignment. PBP is just wrong there.

    qwurx [shmem] ~ > perl -wce 'sub foo{} foo({one=>1,two=>})' -e syntax OK
      Yep. It's just a mistake. A dumb one too, since it was so easy to test. :-(

      Chalk it up to wishful thinking on my part. Or maybe just too much blind faith in the compiler: "It's something that would be easy to test at compile-time, ergo it must be tested at compile-time".

      Still, at least it's not the worst mistake I made in PBP. ;-)

      Damian

      PS: I stand by the overall recommendation though. Error messages that point users to the right place are definitely worth the (tiny) overhead of passing named args in a hash.

        Still, at least it's not the worst mistake I made in PBP. ;-)

        Uh-oh. Whats next? Have a list? ;-)

        I stand by the overall recommendation though. Error messages that point users to the right place are definitely worth the (tiny) overhead of passing named args in a hash.

        Definitely. The recommendation of passing named args in a hash is fine, the one of the MTOW you promote TDI is, if not wrong, then sub-optimal.

        my %hash = ( foo => 1, bar => quux($blorf), ); my $result = frobnitz(\%hash); # ok my $result = frobnitz( %hash); # also ok # --- my $hashref = ( foo => 1, bar => quux($blorf), ); my $result = frobnitz( $hashref); # ok # --- frobnitz( { foo => 1, bar => scalar bar($quux) } ); # not ok

        In the above examples, both of the costly structures for %hash and $hashref are allocated at the pad of the current scope and populated at run time. In the last example, that structures are set up and teared down at every call to frobnitz() :

        use Devel::Peek; sub f{ Dump $_[0] }; f( {o=>1} ) for 1..3; __END__ SV = RV(0x8c17080) at 0x8c17074 REFCNT = 1 FLAGS = (ROK) RV = 0x8bfd7a4 SV = PVHV(0x8c02d04) at 0x8bfd7a4 REFCNT = 1 FLAGS = (SHAREKEYS) ARRAY = 0x8c1bb0c (0:7, 1:1) hash quality = 100.0% KEYS = 1 FILL = 1 MAX = 7 RITER = -1 EITER = 0x0 Elt "o" HASH = 0xc74f0e7f SV = IV(0x8c17060) at 0x8c17064 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1 SV = RV(0x8c17070) at 0x8c17064 REFCNT = 1 FLAGS = (ROK) RV = 0x8c17074 SV = PVHV(0x8c02d04) at 0x8c17074 REFCNT = 1 FLAGS = (SHAREKEYS) ARRAY = 0x8c1bb0c (0:7, 1:1) hash quality = 100.0% KEYS = 1 FILL = 1 MAX = 7 RITER = -1 EITER = 0x0 Elt "o" HASH = 0xc74f0e7f SV = IV(0x8bfd7a0) at 0x8bfd7a4 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1 SV = RV(0x8bfd7b0) at 0x8bfd7a4 REFCNT = 1 FLAGS = (ROK) RV = 0x8c17064 SV = PVHV(0x8c02d04) at 0x8c17064 REFCNT = 1 FLAGS = (SHAREKEYS) ARRAY = 0x8c1bb0c (0:7, 1:1) hash quality = 100.0% KEYS = 1 FILL = 1 MAX = 7 RITER = -1 EITER = 0x0 Elt "o" HASH = 0xc74f0e7f SV = IV(0x8c17070) at 0x8c17074 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 1

        Some of the above pointer being equal is just an artefact of the subsequent call of the same sub (I guess here).

        "It's something that would be easy to test at compile-time, ergo it must be tested at compile-time".

        Maybe the edge case that it's not possible to decide at compile-time if this hash  %h = (  key => @arr ) has a well-formed list at RHS with an even number of elements (how many elements will @arr have ???) makes it not so easy to test it generally at compile-time?

        A good work-around might be to throw a warning if arrays are used as hash-values after a fat comma... I would really like it this way, since list-flattening in values really seems too "odd" to me!

        Cheers Rolf

Re: Named Subroutine Parameters: Compile-time errors vs. Run-time warnings
by ikegami (Patriarch) on May 26, 2009 at 12:31 UTC
    There is
    >perl -ce"sub foo {} fob 'abc'" String found where operator expected at -e line 1, near "fob 'abc'" (Do you need to predeclare fob?) syntax error at -e line 1, near "fob 'abc'" -e had compilation errors. >perl -ce"sub foo {} foo 'abc'" -e syntax OK
    but it doesn't work work with curly args
    >perl -ce"sub foo {} foo { abc => 1 }" -e syntax OK >perl -ce"sub foo {} fob { abc => 1 }" -e syntax OK
    >perl -MO=Deparse -ce"sub foo {} fob { abc => 1 }" sub foo { } do { 'abc', 1 }->fob; -e syntax OK
Re: Named Subroutine Parameters: Compile-time errors vs. Run-time warnings
by LanX (Saint) on May 26, 2009 at 14:16 UTC
    Thanx to Google-Books here the complete passage (underlining added)

    Requiring the named arguments to be specified inside a hash ensures that any mismatch, such as:

    $line = padded({text=>$line, cols=>20..21, centered=>l, filler=>$SPACE});

    will be reported (usually at compile time) in the caller's context:

    Odd number of elements in anonymous hash at demo.pl line 42

    Passing those arguments as raw pairs:

    $line = padded(text=>$line, cols=>20..21, centered=>l, filler=>$SPACE);

    would cause the exception to be thrown at run time, and from the line inside the subroutine where the odd number of arguments were unpacked and assigned to a hash:

    Odd number of elements in hash assignment at Text/Manip.pm line 1876

    The only difference in error handling that I can think of might result from unquoted keys ... (?)

    Cheers Rolf

      The only difference in error handling that I can think of might result from unquoted keys ...
      I don't understand this point. Can you give an example?
        Sorry please forget it.

        It was just a guess that the fat comma => might act differently in curlies than in a parameter list, resulting in another (or missing) quoting behavior of barewords at the LHS.

        But this wouldn't make sense...

Re: Named Subroutine Parameters: Compile-time errors vs. Run-time warnings
by Anonymous Monk on May 26, 2009 at 12:05 UTC
    Can anyone give an example of a compile time error (or warning) associated with this invocation technique, or any explanation or example of what TheDamian was referring to?

    It doesn't exist. Maybe he meant prototypes, who knows :)