in reply to Re: Why are "a ||= b" and "a = a || b" different?
in thread Why are "a ||= b" and "a = a || b" different?

Hmm, not sure I understand the 'one shot' part.

Wouldn't it be simpler to unravel

my($ret) ||= foo();
to
my($ret) = $ret || foo();
and process it accordingly? The way it's implemented now is really confusing to the user.

Replies are listed 'Best First'.
Re^3: Why are "a ||= b" and "a = a || b" different?
by liverpole (Monsignor) on Mar 03, 2007 at 02:38 UTC
    Hi saintmike,

    diotalevi's perfectly right.

    Look at it this way:

    my($ret) ||= foo();

    What you're doing amounts to this, step-wise:

    1. my ($ret); # Create a scalar variable called $ret 2. $ret ||= foo() # If $ret is undefined or zero, assign it to foo( +)

    But of COURSE $ret is undefined when you first declare it, because you haven't assigned it to anything!

    A similarly bad thing is going on here:

    my($ret) = $ret || foo();

    where you're trying to create a new scalar variable my($ret), but its value depends on a previously defined scalar called $ret (notice that's the same variable name?), or foo() if the the value of the $ret was previously undefined or zero.

    If you use strict and warnings (as you always should), then you'll get an error with the second one.  Try this (essentially equivalent) code:

    use strict; use warnings; my $x = $x || 5; print "Now x = $x\n";

    Without strict and warnings you get:

    Now x = 5

    but with them you get:

    Global symbol "$x" requires explicit package name at test.pl line 5. Execution of test.pl aborted due to compilation errors.

    Finally, the reason your title is misleading (as diotalevi suggests), is because it's perfectly fine to do either:

    use strict; use warnings; my $y = 7; my $x; $x = $x || $y; print "Now x = $x\n";

    and:

    use strict; use warnings; my $y = 7; my $x; $x ||= $y; print "Now x = $x\n";

    which will both yield the expected:

    Now x = 7

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      Sorry, liverpole, looks like I totally sent you down the wrong alley.

      It's not about undefined values or using strict.

      If you leave out the my in my post, it makes better sense. What I wanted to say is that

      ($ret) ||= foo();
      is different than
      ($ret) = $ret || foo();
      regarding context.

      Regardless if $ret is defined or not, foo() is in scalar context in the first statement and in list context in the second.

      I've removed the my from the original post to make the intention clearer.

Re^3: Why are "a ||= b" and "a = a || b" different?
by diotalevi (Canon) on Mar 03, 2007 at 18:45 UTC

    No, it would not be simpler to change (a) ||= foo() to (a) = a || foo. I call your attention to Chapter 10 of On Lisp about pitfalls in macros as reference material and in particular, the part about Number of Evaluations. While you originally wrote (a) as your left hand expression, it could have been something else like bar() or (rand < .5 ? $a : $b). If you executed the left hand side multiple times, you could have inconsistent results.

    # (a) could change entirely if run multiple times ( rand < .5 ? $a : $b ) = ( rand < .5 ? $a : $b ); # (a) might have side effects bar() = bar() || foo(); sub bar :lvalue { ... } # (a) might have side effects tie $a, ...; ($a) = $a || foo();

    In all those cases it would be an error to cause the single mention of (a) to expand to multiple mentions. Internally perl evaluates your (a) once. It uses the expression's value as the input to the || operator and then it uses the same already computed expression as an lvalue. To see this under the hood and why, use the B::Concise module to look at your program's structure. Get Idealized optrees from B::Concise to see a simpler view. I annotated the ouput by hand.

    perl -MO=Concise -e '($a) ||= foo()' | idealized-optree leave enter nextstate orassign # ||= gvsv # << $a sassign # scalar = entersub # foo() pushmark gv # *foo

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Thanks, diotalevi, your explanation of the side effects makes perfect sense.

      Do you have any thoughts about why

      ($ret1, $ret2) ||= foo();
      refreshes $ret2 and not $ret1 with the result of the evaluation of foo() in scalar context as mentioned by varian ?

      Update: typo fixed, thx diotalevi.

        You have a typo and meant ||= instead of =||.

        In the case of (A,B) ||= C, (A,B) || ... is the comma operator in scalar context. It evaluates the left argument A, then the right argument B and returns B. Thus you've just written A; B ||= C;.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re^3: Why are "a ||= b" and "a = a || b" different?
by xdg (Monsignor) on Mar 03, 2007 at 02:36 UTC

    I think it matters when $ret is a tied variable and there is some sort of side effect when it is read. The ||= version only evaluates it once, I believe. Esoteric? Yes.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.