http://qs321.pair.com?node_id=11123201

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

Is this correct style? Can be improved without giving $foo a (non empty) default value?

my $fo = q{}; # fo variable is initialized # ... run code, run... # (fo will be updated with a real value in this part) # ... but something forrible happens instead... print "My homework: $fo" ; # prints "My homework: " and exits print 'My homework: ',($fo || 'Roses are red, violets are blue. Fo was + still empty, so I improvised a default value'); # with poetic result +s print 'we have ', ($fo + 2 || 8), 'beans'; # prints: we have 2 beans print 'we have ', ($fo + 0), 'beans'; # prints: we have 0 beans print 'we have ',($fo + 0 || 8),'beans'; # but here prints: we have +8 beans, Why?

Replies are listed 'Best First'.
Re: conditional print. Is correct to use it?
by choroba (Cardinal) on Oct 27, 2020 at 12:04 UTC
    What's the question?

    print "My homework: $fo" ; # prints "My homework: " and exits

    No, it doesn't exit. It continues running the program on the next line.

    print 'My homework: ',($fo || 'Roses are red');

    There are only few values considered false in Perl: an undefined value, empty string, 0, and the string "0". In this case, it's the empty string, so you see the alternative value.

    $fo + 2 || 8
    An empty string in numeric context is treated as zero. 0 + 2 is two which is true, so no need to substitute 8.

    $fo + 0
    See above. 0 + 0 = 0 and that's what we see.

    $fo + 0 || 8
    Again, an empty string in numeric context is 0, 0 + 0 is 0 which is false, so we see 8.

    Modern Perl explains context in a nice and simple way: see Context.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      Your complete list of false values is one of those "well known" facts that I cannot find the documentation for. The list does appear in the second paragraph of the documentation of the function defined, but it does not claim to be Perl's definition of 'false'. Can you suggest a better reference.
      Bill
        See "Scalar values" in perldata.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
        Your complete list of false values is one of those "well known" facts that I cannot find the documentation for.

        There used to be a section in perlsyn, and I wrote the node Truth and Falsehood as a replacement.

Re: conditional print. Is correct to use it?
by davido (Cardinal) on Oct 27, 2020 at 15:25 UTC

    Is your question about style, or about behavior? There's nothing surprising to me about the behaviors you're describing, but maybe there's an expectations vs reality gap.

    You can use Devel::Peek to inspect the actual contents of a scalar.

    You can use perldata to learn about what Perl sees as truth and falsehood.

    You can refer to perlop to see the precedence list, where || is lower than +.

    You can use perl -MO=Deparse,-p,-x9 -e 'print "we have ", ($fo + 2 || 8), " beans";' to see how that parses, and how precedence rules apply. That last one produces:

    perl -MO=Deparse,-p,-x9 -e 'print "we have ", ($fo + 2 || 8), " beans" +;' print('we have ', (($fo + 2) || 8), ' beans'); -e syntax OK

    Dave

      Is more about style. I'm in the middle of an iteration. The known route would be:

      put an if loop before -> to fix $variable -> and then print "something $variable something"... to a file.

      I'm exploring to include the loop directly inside the print line. Start printing "something" to a file -> then include a mini loop to check the current status of the variable and then finish printing "something".

      what strategy would be more correct in your opinion? "if(true){a} else {b}" or just (a||b)

      (are both equally correct?)

      Note: (About why not write just: my $fo; I have a bunch of variables to initialize. Some are expected to be numeric, other will receive a chain. I want to remind who is who. This is the reason to express specifically the empty chain here. (i.e: my $fo = q{}; my $bar = 0;... instead to just write: my ($fo, $bar)

        "if(true){a} else {b}" or just (a||b)

        (are both equally correct?)

        A statement block like if(true){a} else {b} does not evaluate to any value (but see Note 1 below), but an expression like (a||b) always does. Therefore, IMHO it's not correct to say these are equivalent or "equally correct." The if/else-statement block can be made to have an effect equivalent to the expression, but only if the a b expressions or statement(s) have the correct side effects.

        An expression can always be incorporated into another expression — if precedence is handled properly! A statement cannot be incorporated into another statement unless you contort your program logic with some sort of usually unnecessary eval nonsense. E.g.:

        Win8 Strawberry 5.8.9.5 (32) Wed 10/28/2020 15:06:58 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings my $x; # undefined/false my $y = 'default string'; my $side; if (not $x) { $side = $y; } # true clause has side effect # $side = $y unless $x; # a more terse alternative printf "side effect '%s' logical-or '%s' ternary '%s' \n", $side, ($x || $y), $x ? $x : $y; ^Z side effect 'default string' logical-or 'default string' ternary 'de +fault string'

        Notes:

        1. An if- or if/else-statement block like
              if (CONDITION) { statements(s)  }
          or
              if (CONDITION) { statements(s)  } else { statements(s) }
          will return a value from a subroutine if it is the last thing executed in the subroutine, but this behavior is inherent to the behavior of subroutines, and not directly related to the behavior of conditional statement blocks.

          Update: To be more precise, what is returned from a subroutine that has no explicit return statement is the value of the last statement or expression executed in the subroutine. So in the subroutine
          sub func { ... ... if ($x) { foo(); bar(); } else { fee(); fie(); } }
          the return value of bar() is returned by func() if $x is true, that of fie() if $x is false.
          Win8 Strawberry 5.8.9.5 (32) Wed 10/28/2020 22:49:24 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings printf "'%s' returned from true clause \n", func(1); printf "'%s' returned from false clause \n", func(); sub func { my $x = shift; printf "x is %-7s", $x ? 'true' : 'false'; if ($x) { foo(); bar(); } else { fee(); fie(); } } sub foo { return 'from ' . (caller 0)[3]; } sub bar { return 'from ' . (caller 0)[3]; } sub fee { return 'from ' . (caller 0)[3]; } sub fie { return 'from ' . (caller 0)[3]; } ^Z x is true 'from main::bar' returned from true clause x is false 'from main::fie' returned from false clause


        Give a man a fish:  <%-{-{-{-<

Re: conditional print. Is correct to use it?
by duelafn (Parson) on Oct 27, 2020 at 12:01 UTC

    In my opinion, the only thing to do with unexpected values is die:

    # ... but something forrible happens instead... die "Expected a value for fo" unless defined($fo) and length($fo);

    Hard crashes have a way of getting fixed quickly. Anything else tends to cause odd behavior that is hard to track down.

    Note: $fo is initiaslized to an empty string. Use just my $fo; do declare an uninitialized (undefined) value.

    In response to "we have 8 beans", ($fo + 0 || 8) is parsed as ($fo + (0 || 8)) due to precedence. Update: silly me, ignore this.

    Good Day,
        Dean

      > ($fo + 0 || 8) is parsed as ($fo + (0 || 8)) due to precedence.

      It's not.

      perl -MO=Deparse,-p -e 'print $fo + 0 || 8' print((($fo + 0) || 8));

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: conditional print. Is correct to use it?
by kcott (Archbishop) on Oct 28, 2020 at 06:42 UTC

    G'day pvaldes,

    In terms of style, I'd say it's perfectly acceptable to embed an expression in a print list. Having said that, if the expression is long or complicated, I'd usually move it out of the print statement.

    $fo ||= 'Roses are red ...'; print "My homework: $fo";

    As others have already pointed out, a FALSE value can be "non empty", so || is probably not the best choice.

    # (fo will be updated with a real value in this part)

    You'll need to tell us what's going on there. My first guess was that you're perhaps doing something like:

    $ perl -E ' my $fo = ""; update($fo); print "|$fo|"; sub update { my ($arg) = @_; $arg .= "XXX"; } ' ||

    when what you really wanted was something like:

    $ perl -E ' my $fo = ""; update(\$fo); print "|$fo|"; sub update { my ($arg) = @_; $$arg .= "XXX"; } ' |XXX|

    But there would be dozens of reasons why your update could be returning a "forrible" value. :-)

    — Ken

      ... a FALSE value can be "non empty", so || is probably not the best choice.

      I don't understand this statement. (Update: hippo has likely shown the way to understanding. :) Is there any false value of $x, empty or not, for which the expression ($x || $y) would not evaluate as the value of $y? Likewise, given the statement
          $x ||= $y;
      is there any false initial value of $x for which the final value of $x would not end up as $y?


      Give a man a fish:  <%-{-{-{-<

        I think kcott is saying that you might, in some conditions, wish to retain the non-empty false value in $x rather than discard it in favour of $y. eg. if 0 were a valid input for $x and you had:

        $x = 0; $y = 10; $number = $x || $y; print "$number\n";

        You would see 10 rather than the valid-but-false 0. In such cases $number = $x // $y; might be more appropriate.


        🦛

        Pretty much what ++hippo said. :-)

        length will return FALSE for both '' and undef, and TRUE for both 0 and '0'. It's first documented as doing that in 5.12.0; however, there was a bug that was fixed in 5.14.0 (perl5140delta: Syntax/Parsing Bugs) so I'd be more comfortable with both defined and length if using anything earlier than 5.14.0.

        # 5.14.0 or later $ perl -E 'my ($x, $y) = (0, "fallback"); $x = length $x ? $x : $y; sa +y $x' 0 $ perl -E 'my ($x, $y) = ("0", "fallback"); $x = length $x ? $x : $y; +say $x' 0 $ perl -E 'my ($x, $y) = ("", "fallback"); $x = length $x ? $x : $y; s +ay $x' fallback $ perl -E 'my ($x, $y) = (undef, "fallback"); $x = length $x ? $x : $y +; say $x' fallback # 5.12.0 or earlier $ perl -E 'my ($x, $y) = (0, "fallback"); $x = (defined $x && length $ +x) ? $x : $y; say $x' 0 $ perl -E 'my ($x, $y) = ("0", "fallback"); $x = (defined $x && length + $x) ? $x : $y; say $x' 0 $ perl -E 'my ($x, $y) = ("", "fallback"); $x = (defined $x && length +$x) ? $x : $y; say $x' fallback $ perl -E 'my ($x, $y) = (undef, "fallback"); $x = (defined $x && leng +th $x) ? $x : $y; say $x' fallback # Probably not what was intended $ perl -E 'my ($x, $y) = ("0", "fallback"); $x ||= $y; say $x' fallback $ perl -E 'my ($x, $y) = ("", "fallback"); $x //= $y; say "<$x>"' <>

        — Ken

Re: conditional print. Is correct to use it?
by jcb (Parson) on Oct 28, 2020 at 02:12 UTC

    I typically initialize variables explicitly to undef (as in my $var = undef;) if I cannot produce a meaningful initial value at that point in the program. If this is done, then the test to later determine if a value was produced is simply defined($var) and usually producing an undefined value is just as bad as producing no value, so die "..." unless defined($var); works well to bail out on error later.

    And just a reminder, you are including use strict; and use warnings;, right?

      yep. use strict; use warnings; and use criticism 'brutal'; :-) I'm reviewing my old scripts