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

The ||= only gets one shot to evaluate the left side and since it needs a boolean, it does it in scalar context. The context of the right hand side is determined by the left side so foo() is scalar too.

You really ought to update your node title though. It's misleading. The central question is about context and you'd want to use a title like "Why are "(a) || b" and "(a) = a || b" different."

⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

  • Comment on Re: Why are "a ||= b" and "a = a || b" different?

Replies are listed 'Best First'.
Re^2: Why are "a ||= b" and "a = a || b" different?
by saintmike (Vicar) on Mar 03, 2007 at 01:08 UTC
    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.
      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.

      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.

      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.

Re^2: Why are "a ||= b" and "a = a || b" different?
by saintmike (Vicar) on Mar 03, 2007 at 06:31 UTC
    you'd want to use a title like "Why are "(a) || b" and "(a) = a || b" different."
    I guess you mean

    Why are "(a) ||= b" and "(a) = a || b" different

    and you're right that it's slightly misleading, but I could argue that "a" is a place holder that can hold anything including (a) :).