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

Today I wrote a script, in which I preferred some logic expressed as 2nd fragment rather than the 1st:

for ( $x .. $y ) { # some loop # ... skipped ... # fragment #1: next unless exists $gs_val-> { HT }; my $ht = $doc-> getValue( $gs_val-> { HT }); next unless exists $ht-> { Default }; my $df = $doc-> getValue( $ht-> { Default }); next unless exists $df-> { TransferFunction }; my $tf = $doc-> getValue( $df-> { TransferFunction }); next unless $tf eq 'Default; # ... skipped ... # fragment #2: next unless $doc-> getValue( $doc-> getValue( $doc-> getValue( $gs_val-> { HT } || next ) -> { Default } || next ) -> { TransferFunction } || next ) eq 'Default'; # ... skipped ... }

As you see, I tried to improve the #1 with a few blank lines, but, to me, the #2 is more readable -- now, and I hope it will remain so in a year, when I have to try hard to remember what it all was about. Perhaps not everyone will agree.

However, while experimenting and testing to see if such constructs are OK, I was surprised to find that Perl prefers even more unexpected form:

>perl -we "for(\0,0){print${$_||next}}" 0 >perl -MO=Deparse -we "for(\0,0){print${$_||next}}" BEGIN { $^W = 1; } foreach $_ (\0, 0) { print ${next unless $_;}; } -e syntax OK >perl -we "for(\0,0){print${next unless $_}}" 0 >

Looks like if "or next" is part of block that is supposed to return value, then Perl replaces it with "next unless" syntax. What's totally unexpected is that this "next unless" thing is actually valid and does return value.

I can only guess that everything works the other way, in reality: "next unless" is optimized to "or next", therefore it returns value, and that for some strange reason B::Deparse shows "||next" as "next unless". That is only way this can be sane... Or is it not so? :)

Replies are listed 'Best First'.
Re: Abuse of "or next" in expressions and "next" that returns value
by RonW (Parson) on Jul 18, 2018 at 21:00 UTC

    next is not a function, so does not return a value. Nor does it need to return a value. It is either executed or not. If executed, the expression containing it never gets a chance to use anything next didn't provide. If not executed, then the other subexpression had a non-false value which became the value of the containing expression.

    In this case, ${next unless $_;}, if $_ is non-false, the evaluator skips everything to the left of unless, so next isn't executed.

    I do agree that ${next unless $_;} is surprising syntax. Still, it is valid and semantically equivalent to $_ or next

      Thank you everyone for answers, now it became more clear. As I understand, every statement in Perl "returns" a value -- rather, it can be evaluated if program flow requires it. The bare "next;" is a statement. What it evaluates to is never necessary to know. The "foo unless bar;" is a statement, too. If "bar" is false, it obviously evaluates to "foo". But if "bar" is true, this whole statement, unexpectedly to me as of yesterday, evaluates to "bar":

      >perl -wE "$x=33; say do{42 unless $x}" 33

      The (wrong) intuition was that result would be something like "undef". But, if I get RonW's explanation right, the "evaluator" is more straightforward and "primitive" -- it doesn't try to "understand" statement as a whole, but whatever subexpression was evaluated last is taken as result for a whole.

Re: Abuse of "or next" in expressions and "next" that returns value
by AnomalousMonk (Archbishop) on Jul 18, 2018 at 21:01 UTC

    I can't comment on the internals of the deparser, but...

    ... to me, the #2 is more readable ...

    Everyone has their own notion of what is understandable and readable. To me, fragment 1 is way more readable, understandable and maintainable — today, tomorrow, in a year, in a decade.

    The only circumstance in which I might deploy something like fragment 2 is if the simple-seeming code of fragment 1 actually harbored some perverse and devilish subtlety that I did not trust myself to convey clearly in a comment (or if I didn't trust the maintainers to pay attention to comments). In such a case, I might write my code in such a way as to bring casual perusal to a screeching halt and cause a reader to proceed only with the greatest trepidation and attention to each detail; a huge mental speed-bump as it were. But I normally try to avoid writing speed-bumpy code.

    Update: A few minor wording changes.


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

Re: Abuse of "or next" in expressions and "next" that returns value
by Veltro (Hermit) on Jul 18, 2018 at 21:08 UTC

    Hi vr,

    ...unexpected is that this "next unless" thing is actually valid and does return value

    I'm sorry but I don't understand where you get that next unless returns a value?

    next is just like a continue statement in C. If it is called, it just jumps out of whatever it is doing and transfers control to the for loop.

    ... || next is AFAICS 100% equivalent to next unless ...

    In your for loop you go through a scalar reference to 0 so the scalar that is tested is NOT 0 (or in other words NOT false). The value is valid so the next is not executed. The value is dereferenced to 0 and is printed to the console. The next loop you execute is with the value 0 and in the statement is interpreted as 0 (or in other words false). So the || statement will execute the next command which is next ;. All execution is interrupted and control is transferred to for and nothing is printed.

    I hope this makes sense

    Veltro

    Edit: One more thing that I noticed:

    next unless exists $ht-> { Default };

    is not the same as:

    ... ->{ Default } || next

    In the first fragment next will only be executed in case the Default value does not exists. In case ...->{ Default } returns 0 then next will also be excuted. So be carefull with that.

Re: Abuse of "or next" in expressions and "next" that returns value
by haukex (Archbishop) on Jul 19, 2018 at 18:34 UTC

    I think it's worth noting that both perl -MO=Concise -e 'do {next unless $x}' and perl -MO=Concise -e 'do {$x||next}' produce exactly the same output (at least on v5.26):

    9 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 - <1> null vK*/1 ->9 8 <@> leave vKP ->9 3 <0> enter v ->4 4 <;> nextstate(main 2 -e:1) v:{ ->5 - <1> null vK/1 ->8 6 <|> or(other->7) vK/1 ->8 - <1> ex-rv2sv sK/1 ->6 5 <$> gvsv(*x) s ->6 7 <0> next v* ->8

    See the last four lines in particular. If there's no difference in the ops, then B::Deparse, which "generates perl source code based on the internal compiled structure", probably won't be able to tell the difference either.

    Minor edits.

A reply falls below the community's threshold of quality. You may see it by logging in.