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

very simple question. what is the proper perl way to have multiple statements follow an "and" or an "or":
($istrue) or { print "this is bad"; next; } ($istrue) or print "this is bad", next;

Replies are listed 'Best First'.
Re: and multiple statements
by ikegami (Patriarch) on Jun 06, 2010 at 06:32 UTC
    print "this is bad", next;
    means
    print("this is bad", next);

    Disambiguate with parens

    ($istrue) or print("this is bad"), next; ($istrue) or (print "this is bad"), next;

    Or use do as previously shown

    ($istrue) or do { print "this is bad"; next; };

    But an if would be clearer.

    if (!$istrue) { print "this is bad"; next; }

    Update: Added missing "!" as per reply.

      if (!$istrue)  { print "this is bad"; next }
      But an if would be clearer.
      Is it? Always? For everyone?

      Considering the idom open ... or die ... is quite common, I'd say that

      open ... or do {...;...}
      is clearer than
      if (!open ...) { ...; ... }
      I'd certainly write the former.
        It's quite common because it's required in the case of open(my $fh, ...), not because it's clearer.
Re: and multiple statements
by jwkrahn (Abbot) on Jun 06, 2010 at 02:14 UTC
    $istrue or do { print "this is bad"; next; };
Re: and multiple statements
by BrowserUk (Patriarch) on Jun 06, 2010 at 11:53 UTC

    This seems clear enough to me, but others will take PBP line:.

    for( 1..10) { $_ & 1 and warn "odd" and next; print "$_ is good and even" };; odd at (eval 7) line 1, <STDIN> line 6. 2 is good and even odd at (eval 7) line 1, <STDIN> line 6. 4 is good and even odd at (eval 7) line 1, <STDIN> line 6. 6 is good and even odd at (eval 7) line 1, <STDIN> line 6. 8 is good and even odd at (eval 7) line 1, <STDIN> line 6. 10 is good and even

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      While ok for your example code, I generally dislike this style because:

      • It tends to be error-prone in that it is easy to silently ignore error returns. For example, replace warn above with a function that may return failure.
      • It doesn't scale well as the code is maintained. How should you change the code above, if in addition to calling warn, you want to do something else? Using a block if or block do {} construct, you simply add a new statement, a less tricky one line change that's easier to review in revision control.

        For example, replace warn above with a function that may return failure.

        The use of warn or print and next pretty much defines the idiom.

        I can see that I might substitute some call to a logger() for warn, but not much else. And if the logger() in question failed to return true, then I would wrap the logger() in a myLogger() that did.

        It doesn't scale well as the code is maintained.

        This is the same argument used against postfix if, for, while, until etc. etc. The "Just in case" argument. And (IMO) it's a crock.

        By the logic behind it, you would code every single expression or statement in your program within a do block automatically, just in case that statement or expression needs to become two at some later point in time.

        But what about the statements within those do blocks. Shouldn't you also put each of them inside it's own do block also? Just in case ....

        How should you change the code above, if in addition to calling warn you want to do something else?

        Very simply, I change the <cond> or warn and next to

        if( cond ) { warn; otherStuff(); moreStuff(); next; }

        Just as  x() if cond(); becomes if( cond) { x(); y(); } etc.

        I don't find the effort of such changes onerous. Indeed, they seem to me to be a programmers job.

        Besides which, generally, once programs get beyond the 'in the process of being written' stage, you don't normally suddenly discover that you 'forgot' to call a function or assign a variable.

        I invariably find that the need to make such changes (when in maintenance), means that a significant change to the spec or algorithm is involved. And when that happens, the 'minimal change sets in maintenance' school of thought is counter-productive. It encourages the maintenance programmer to attempt micro-surgical changes to the code, rather than consider the effect of the change in the bigger picture, and perhaps refactor.

        If you've ever had the pleasure of working on a mature code base that has evolved for a long period under a minimal change sets regime, you'll know just what a convoluted mess of temporary flags; multi-condition returns and goto-style spaghetti (even if it is implemented with out-of-line one-off subroutines, method calls or subclass overrides) that can lead to.

        I've described here before a situation where I was allocated a bug that had existed in a mature code base for years in which my (working) solution was to replace 900+ lines with <100. The change was rejected as it was "too big". I tried to argue the case that the bug in various guises had existed for many years; had been the subject of 20+ attempts to fix it in that time; that most of those 900 lines were copy&paste&modify-one-or-two-lines repetitions; all of which fell on deaf ears. Despite that my change passed all the existing tests, and the failing testcase; and an elaborate testcase I added. But the latter was deemed "too big for inclusion" also. That was the only time I ever terminated a contract early. By mutual agreement, with notice period etc.; but at my instigation.

        So, I don't find the effort of switching from cond and warn and next; to if( cond ) { something; warn; next } any kind of a barrier to using the former until the latter if required. Nor switching back if the latter is no longer needed. The time taken to physically type the change pales into insignificance when considered in the light of the time taken to access that the change is required. Whilst I have a strong preference for concise code, it is because I believe that there is a strong correlation between 'brevity' and 'clarity'; and that more code means more bugs. It is rarely, if ever, influenced by keystroke counts.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

      I agree, 2¢ in the kitty. This is the style I prefer and gravitate toward. I tend to use names too so I think it would even read well to those who don't like this style.

      NUM: for( 1..10) { $_ & 1 and warn "odd" and next NUM; print "$_ is good and even\n"; }
        I tend to use names too

        I only use names in nested loops where I need to break out of nested levels. And I think I've had that arise maybe twice in 8 years.

        Generally, these kind of short circuit guards tend to be at (or very close to) the top of the loop body they short-circuit, which (IMO) renders the labels redundant.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.