Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Forcing list context of passed parameters

by papidave (Pilgrim)
on Sep 28, 2007 at 21:16 UTC ( [id://641613]=perlquestion: print w/replies, xml ) Need Help??

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

Update:

As-of sidhekin's note on "Don't use it if you don't mean it", the selected resolution of omitting the prototype from foo -- which I was actually doing to make things work -- is now established as the de facto standard. Following C standards for Perl is clearly a dangerous path to follow.

If the lecture Gratuitous use of Perl Prototypes hadn't ended with a self-satisfied snigger, I might have even ++'ed it.

Correction "4" => "four", typo cut/paste from an earlier version of foo.pl, where I had passed it through the gotse operator as an attempt at listifying it.

-- snip --

Here's one that had me scratching my head for some time. Background follows:

  1. I have a function definition that takes a list, does some "stuff" to the first element, and returns the revised list:
    sub foo($@) { my ( $first, @other ) = @_; # do something to $first return( $first, @other ); }
  2. The list I want to pass to foo() is somewhat hairy, so I decided to protect it with qw(), e.g.:
    print foo( qw( one two three four ) ), "\n";
  3. When executed, however, I don't get what I wanted, which is a catenation of ("munged one", "two", "three", "four"). Instead, I get "four".
  4. At this point much head-scratching occurs, until I realize that the prototype for foo() is forcing my qw into a scalar context. Remove the prototype, and bingo! it works.

The problem that I have with this is that I like to use prototypes, as a form of self-documenting code. I even use them in object methods, where they don't get enforced, because I want the reader to know what arguments I expect to receive. Ideally, what I want is to list-ify the qw() results, so they'll be passed through foo() intact.

My copy of the Camel asserts (vol 3, p.778):

There's no "list" function corresponding to scalar since, in practice, one never needs to force evaluation in a list context.

I already have multiple potential workarounds -- changing or eliminating the prototype, isolating "one" from the qw() list, etc. -- but it just doesn't seem "right" to have to do this. I see that Perl6 (http://dev.perl.org/perl6/rfc/175.html) may have a "list" keyword that would solve this cleanly.

Does this mean that I'm really running into a pathological edge case in Perl5, or am I just being boneheaded in my approach to the problem? Is there some (reasonably elegant) way to list-ify the args before they get passed to foo()?

Replies are listed 'Best First'.
Re: Forcing list context of passed parameters
by grinder (Bishop) on Sep 28, 2007 at 21:38 UTC
    Does this mean that I'm really running into a pathological edge case in Perl5

    Something like that.

    You're using prototypes.

    Ditch the ($@) on the function declaration, and everything will be fine. Perl will do what you think it should do.

    sub foo { my ( $first, @other ) = @_; # do something to $first return( $first, @other ); }

    The rule I remember is this: "Only use prototypes if you are overloading CORE functions." That might not be sufficiently subtle, but it had stood me in good stead so far.

    • another intruder with the mooring in the heart of the Perl

Re: Forcing list context of passed parameters
by Sidhekin (Priest) on Sep 28, 2007 at 21:42 UTC

    A prototype is not just documentation. Don't use it if you don't mean it!

    Having said that, here are two suggestions:

    # honour the prototype: print foo( one => qw( two three four ) ), "\n";

    Since you consider the prototype valuable as documentation, you will likely value the documentation of setting the first argument apart in the call as well.

    # bypass the prototype: print &foo( qw( one two three four ) ), "\n";

    While not recommended in this situation, it may be useful with arguments from other expressions, particularly function calls:

    # bypass the prototype: print &foo( bar(@args) ), "\n";

    But as you've been told, it is likely better to just give up this use of prototypes.

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

      Sidhekin and others wrote:
      just give up this use of prototypes
      This would seem to be the consensus. Having suffered through years of mismatched parameters in C, and having been rescued by good parameter checking when the ANSI standard finally came out for the platforms where I was working, a habit was formed that didn't map well into this environment.

      I had a good chat with mr_mischief regarding http://perldoc.perl.org/perlsub.html#Prototypes and in particular the section referencing retrofits that explains the root cause on my list getting eaten.

      Having been soundly downvoted (ouch!), I guess I will be forced to follow more closely to the docs:

      This is all very powerful, of course, and should be used only in moderation to make the world a better place.
      and leave my lack of moderation to other vices.

        Prototypes in perl aren't parameter checking, they're hints to the compiler that you're trying to mimic the calling convention of a builtin. If you still want parameter checking look into something like Params::Validate.

Re: Forcing list context of passed parameters
by almut (Canon) on Sep 28, 2007 at 22:06 UTC
    ... Instead, I get "4".

    Not that it matters much in the overall picture of things... :)  but I do get "four" in $first, not "4" — as in

    my $first = qw( one two three four ); # "four"

    as opposed to

    my $first = @{[qw( one two three four )]}; # 4

    Apart from that, I'd just ditch the prototypes, or put them in a comment if you really want them for documentation purposes...

Re: Forcing list context of passed parameters
by Anonymous Monk on Sep 28, 2007 at 21:34 UTC
Re: Forcing list context of passed parameters
by chromatic (Archbishop) on Sep 29, 2007 at 00:45 UTC
    The problem that I have with this is that I like to use prototypes, as a form of self-documenting code. I even use them in object methods, where they don't get enforced, because I want the reader to know what arguments I expect to receive.

    I find that well-chosen names is a better form of documentation than anything else.

Re: Forcing list context of passed parameters
by eyepopslikeamosquito (Archbishop) on Sep 29, 2007 at 20:44 UTC

    In case you need any more persuading, Perl Best Practices guideline 9.10 states: Don't use subroutine prototypes.

    If you don't already own this book, notice that this guideline, and the rationale behind it, can be freely viewed online as part of the free sample chapter.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (2)
As of 2024-04-26 04:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found