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

RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc

by LanX (Canon)
on Sep 01, 2012 at 21:39 UTC ( #991197=perlmeditation: print w/ replies, xml ) Need Help??

sub swap { my ($a, $b) = \(@_); ( $$a, $$b ) = ( $$b, $$a ); } my ($x,$y)=(42,666); $\="\n"; print "($x,$y)"; #> (42,666) swap($x,$y); print "($x,$y)"; #> (666,42)
is that idea new?

update:

for completeness, this works too:

sub swap { my $a = \shift; my $b = \shift; ( $$a, $$b ) = ( $$b, $$a ); }

Cheers Rolf

UPDATE: I was asked if this example accomplishes anything that the following doesn't:
sub swap { ($_[0], $_[1] ) = ( $_[1], $_[0] ); }
no it doesn't ) I just wanted to meditate about a pattern to have named aliases.

Compare PBP's critic about using unreadable variables like $_[0].

of course swap() is too short to profit from named arguments.

) except by not dieing on literals.

Comment on RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc
Select or Download Code
Re: RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc
by philiprbrenan (Monk) on Sep 01, 2012 at 21:42 UTC

    I don't think so.

Re: RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc
by BrowserUk (Pope) on Sep 01, 2012 at 21:48 UTC

    Essentially the same trick was employed nine years ago by Juerd for his Non-destructive array processing.

    And I reused it 9 months later in Re: Re: reference to an array slice?.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

      well using parts of it doesn't make it an idiom or pattern.

      can't see any \( ) in juerd's code and the other examples look like obfuscation contest for me not like an advice for an idiom.

      Cheers Rolf

        Looking again, I see you are right. Whilst related, they are not the same usage.

        By way of penance:

        sub swap{ our($a,$b); local( *a, *b ) = \( @_ ); ($a, $b ) = ( $b, $a ) } my( $x, $y ) = ( 1, 'fred' ); swap( $x, $y ); print "$x, $y";; fred, 1 print swap( 1, 'fred' );; fred 1

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        RIP Neil Armstrong

Re: RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc
by davido (Archbishop) on Sep 01, 2012 at 23:47 UTC

    I've got two problems with the whole thing that would prevent me from ever accepting it as a "common idiom".

    First, the function has a side effect: It modifies its parameters. I understand, that's its primary purpose: to modify its parameters. But this isn't C++; Perl makes it so easy to pass complex return values that idiomatically, it's rare to see a Perl subroutine that intentionally modifies its parameter list (unlike C++, where it's a common idiom to pass objects by reference and manipulate them so that the side effect propagates back through the parameter list). This is why PBP encourages unpacking @_, not just because it looks prettier inside the subroutine, but because it reduces the potential of surprising someone who wasn't watching out for parameter side effects. Writing a Perl subroutine that modifies its parameters violates the principle of least surprise.

    Second, as long as we're working so hard to modify the parameter list, let's make the following a fatal error:

    swap( qw(this that) );

    The first two versions don't throw an error when swap is called with static parameters. The third one does.

    But they all have a return value of the swapped list, so they're useful even when modifying the parameter list isn't the goal, right? Well, sort of. All three swap functions produce a reasonable return value. As soon as you decide it's Ok to use the subroutine for its return value instead, and write something like this:

    my @swapped = swap ( qw(this that) );

    ...which is fine for the first two examples, you're going to forget, and go ahead at some point using it like this:

    my @swapped = swap( $variables_that_should, $not_be_swapped )

    Not only would this idiom need a #comment whenever the subroutine is defined, it would need a comment whenever it's used reminding the reader that in one case we're intentionally modifying the parameters, or in another case we shouldn't use the parameters ever again because they just got modified even though we're capturing a return value.

    Regardless of whether or not you happen to be Ok with subroutines that modify their parameter list, one that does it through an extra layer of indirection is even sneakier, and one layer harder to skim as someone reads through the code later on. Though I don't like any of them, at least the last one will throw an error if you attempt to swap literals, and at first glance looks like it does exactly what it does: It acts upon @_, which is an alias. If we ignore the advice of avoiding the pitfall of modifying the parameters, I believe it's a particularly bad practice to follow the PBP suggestion of unpacking the parameter list. It lulls the casual reader into a sense of false security. If PBP had a rule that permitted modifying the parameter list, it would (or should) say to do so in a visually unambiguous way.

    (Think how many times we see this in a newbie's code: while( my line = <DATA> ) { my $result = chomp $line; ... } )


    Dave

      Thanks for your well thought reply.

      Can't say much about the literals and the missing error, I think swapping literals doesn't make much sense ... (well maybe in LISP).

      Anyway much of your reasoning is about manipulating arguments is bad practice.

      Actually perl has plenty of bultins which do that and many lamda-patterns use this feature (i.e. map/grep {BLOCK} like commands where at least $_ is aliased) and if you look closely into perl6-signature you will find the attribute "rw" for exactly this purpose.

      So I can understand if manipulating aliases is not OK for _your_ coding practice, but perl is a multi-paradigm language, where other want to implement their own patters.

      For me using two $-sigils like in "$$a" is indication enough that this is no normal assignment, and certainly better to read and more obvious than spreading $_[n] around a 50 line long sub.

      update:

      I can show you old CORE modules which are programmed w/o unpacking variables and are very hard to maintain. Automatically refactoring $_[n] into $$name would instantly work, even on LHS of assignments and improve readability.

      Cheers Rolf

      pop(), push(), shift(), unshift(), delete(), splice() ...
      > Second, as long as we're working so hard to modify the parameter list, let's make the following a fatal error: swap( qw(this that) );

      Thanks for pointing out this subtle difference!

      After some meditation I actually I prefer this not being a fatal error!

      The (lisp like) possibility of having references to literals always confused me to hell.

      Back in time when LISP was running on 4 KB RAM it certainly made sense to avoid copies, but nowadays?

      this little experiment shows whats happening when dereferencing a ref to a literal

      $x=\1; $y=$$x; # equivalent to $y = 1, i.e. a copy $y++; print $y; #> 2

      it's a copy and I thinks thats saner.

      The other point is what a modifier-routine should return.

      > But they all have a return value of the swapped list, so they're useful even when modifying the parameter list isn't the goal, right?

      There are many possibilities:

      - dual use by returning the swap => fault tolerance

      - empty return to avoid double use => explicit use, ignore error

      - carping if defined wantarray. => runtime warnings

      But actually these design decisions are out of my scope, cause it doesn't inflict the idiom, cause it's the same problem when using $_[0] aliases etc.

      for simplicity I'd simply add an empty return() to the example:

      sub swap { my ($a, $b) = \(@_); ( $$a, $$b ) = ( $$b, $$a ); return; }

      Cheers Rolf

        When I first /msg'ed you to inquire whether you saw any differences, I at the time didn't either, and it wasn't until I investigated further that I realized there should be a fatal runtime error (and there is) with the non-unpacked version.

        Anyway, you present good arguments, but I am stubborn against considering it a new Perl idiom that should find its way into real code. I do like Moritz's "use perl6" version, because of how explicit it is, but still have concerns. :)

        Anyway, great RFC, as it produced good comments.


        Dave

Re: RFC: Idiom for named read-write arguments (aliases) instead of using $_[0] etc
by moritz (Cardinal) on Sep 02, 2012 at 10:15 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://991197]
Approved by Corion
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (2)
As of 2014-09-20 03:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (152 votes), past polls