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

Argh, I did it again! I used shift like this:

shift; do something with $_;

and then spent longer than I care to admit wondering why I wasn't getting the milage I expected. What I needed of course was:

$_ = shift; do something with $_;

Why doesn't shift (and similar usage) assign to $_ if no lvalue is specified? It doesn't DWIM!


Perl is Huffman encoded by design.

Replies are listed 'Best First'.
Re: shift doesn't DWIM
by davido (Cardinal) on Jul 12, 2005 at 05:37 UTC

    Where would the line be drawn? Would using map in "void context" spew its return value into @_ ?

    DWIMery shouldn't venture into the "Do What I Wish" if doing so creates serious language inconsistancies, IMHO.

    My memory may be incomplete or in need of refreshing, but I can't think of any subs that populate $_ as a default lvalue. Yes, there is the diamond operator in the special case of a while() loop, there is foreach(), the m// and s/// operators, and there are subs that utilize $_ as a default parameter. But there aren't any subs I can think of that populate $_ as though it were an lvalue.


    Dave

      Yes, and Perl 6 even the special while loop exception is going away. Instead you typically use topicalizers to set $_:
      given shift { do something with $_; }
      or
      for @ARGS { do something with $_; }
      And in particular, while on a filehandle turns into a (lazy) for:
      for =$fh { do something with $_; }
      In general, the only dwimming on void context in the standard Perl 6 dialect will be deciding whether to turn an unthrown exception (aka undef) into a thrown exception because someone forgot to check the return value.
      Where would the line be drawn? Would using map in "void context" spew its return value into @_ ?

      split does exactly that, but that use is deprecated: http://perldoc.perl.org/functions/split.html . So I certainly doubt that such a feature will be added to map. ;-)

      My memory may be incomplete or in need of refreshing, but I can't think of any subs that populate $_ as a default lvalue. Yes, there is the diamond operator in the special case of a while() loop, there is foreach(), the m// and s/// operators, and there are subs that utilize $_ as a default parameter. But there aren't any subs I can think of that populate $_ as though it were an lvalue.

      Interesting question. I'm also having a hard time thinking of such functions...

      map and grep assign to an implicitly localized $_ (as does foreach if no loop variable is given), but not their results.

      The function readline clobbers $_ if it is in the test clause of a while loop:

      % date|perl -le '$_="x";while(readline *STDIN){printf uc};print"<$_>"' TUE JUL 12 01:56:04 EDT 2005 <>

      It wouldn't surprise me if there are more examples, but I can't think of any ATM.

      the lowliest monk

Re: shift doesn't DWIM
by anonymized user 468275 (Curate) on Jul 12, 2005 at 07:00 UTC
    I would say it's because of three things:

    1) $_ is often treated as a default argument to a function, for example for chop and as a default second argument for split. This in no way implies it should be a default LHS to an omitted assignment, just because shift takes an array argument rather than a scalar.

    2) shift does have a default argument, @_, because it takes an array argument rather than a scalar.

    3) using $_ as a default argument is a reasonably safe implementation, because it is applied only when the relevant argument is explicitly omitted from an actual function call; in other words is free from ambiguous meaning (including in a DWIM sense). But the intended meaning for an omitted assignment to a function call is that its return value is available for direct evaluation (e.g. MyFunc(shift())), which may be delayed to the next statement for some cases, but either way is never assigned to $_. So the precedent is in fact never to do what is suggested in the OP.

    3) (conclusion) the proposed implementation is unsafe because $_ is intended to preserve its contents across scope and context boundaries, unless explicity assigned otherwise (omission of required arguments being deemed explicit enough), including for functions. The implementation proposed in the OP would therefore destroy the main purpose of $_.

    One world, one people

Re: shift doesn't DWIM
by tlm (Prior) on Jul 12, 2005 at 09:45 UTC

    There are enough times when one just wants to get rid of the first element in an array, via:

    shift @array;
    It would be a nuisance to have to create a spitoon-like variable just to catch the shift'ed element (thereby preserving $_). And it would be easy to forget to do this, leading to many chomped butts.

    the lowliest monk

      I agree entirely. From a Huffman-encoding viewpoint, I'd say doing shift @a to discard the first element of the array and doing $_ = shift @a to put that discarded element in $_ is a far better allocation of characters than () = shift @a for discarding and shift @a for storing in $_. Not to mention those empty LHS parens look odd to some folks.

      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

        Very nice reply! With a rationale like that prodding my brain maybe I won't fall into this particular hole that I've invented for myself again.


        Perl is Huffman encoded by design.

      Not to mention it's analogous to the behavior of shift in shell scripts which is often used in this way (just a bare shift to toss the value in $1 after retrieving it previously; especially in getopt-ish code).

      --
      We're looking for people in ATL

Re: shift doesn't DWIM
by GrandFather (Saint) on Jul 12, 2005 at 20:31 UTC

    Thank you all. The original post was a bit low grade, but there has been some rather enlightening discussion.

    Knowing a reason for a design decision often helps me remember how things hang together.


    Perl is Huffman encoded by design.