Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

I don't buy the argument that "unless" "reads better" than "if !" (nor that "reads better" is always a good criteria for evaluating code, especially not "reads more like English prose").

I do buy the argument that making the negation more subtle (by using "unless" instead of "if !") also means that you increase the odds of mentally dropping the negation and either introducing a bug or wasting too much time being confused (having experienced this and having seen others do it and having seen quite a few bugs make it to Production that were introduced via 'unless').

Another unfortunate side-effect of that "subtle 'not'" that I was a bit surprised and dismayed to repeatedly see in real code recently, was constructs like:

unless( $a ne $b ) {

and worse. The 'not' is subtle so once you start using 'unless' all over the place you'll likely start introducing double negatives.

So I have a strong preference for:

if( $a eq $b ) { # or if( $a ne $b ) {

over anything using 'unless'.

Which brings me to a subject we were discussing this week at work. I've heard (or read) a lot of people agreeing that "of course" you should not use 'unless' with complex logical expressions. But that seems to me to be the one place where I can see 'unless' having some value:

croak( "You must pass foo() a non-empty array ref" ) unless $arg && 'ARRAY' eq ref($arg) && 0 < @$arg;

Directly turning that 'unless' into an 'if' gives us:

croak( "You must pass foo() a non-empty array ref" ) if ! $arg || 'ARRAY' ne ref($arg) || 0 == @$arg;

Now, I don't personally find that particular 'if' obviously worse than the prior 'unless'. But I acknowledge that it is more complex and can see it being harder or slower to parse for many. So I'm somewhat reluctant to make such a transformation when refactoring code.

And it can get "worse":

croak( "You must pass foo() a non-empty array ref or hash ref" ) unless 'ARRAY' eq ref($arg) && 0 < @$arg || 'HASH' eq ref($arg) && 0 < keys %$arg;

This would require adding parens, which can reduce readability considerably (not terrible in this example, though -- certainly more complex):

croak( "You must pass foo() a non-empty array ref or hash ref" ) if( ( 'ARRAY' ne ref($arg) || 0 == @$arg ) && ( 'HASH' ne ref($arg) || 0 == keys %$arg ) );

So, I'm more of the opinion that 'unless' should only be used in the case of complex logical expressions. That is, only for complex logical expressions where the 'unless' version is significantly simpler than the 'if' version.

For the sake of completeness, I'll note that I often find value in removing the complex logical expression (which also removes any benefit to using 'unless', IMO). In the above example, if we get an error about "You must pass foo() a non-empty array ref or hash ref", then we have several possible cases to consider and this could add a step or two to debugging that might add significant time to the process. So it might be better more like:

croak( "You didn't pass foo() a reference" ) if ! $arg || ! ref $arg; if( 'ARRAY' eq ref $arg ) { croak( "You passed foo() an ARRAY ref but it was empty" ) if 0 == @$arg; ... } elsif( 'HASH' eq ref $arg ) { croak( "You passed foo() a HASH ref but it was empty" ) if 0 == keys %$arg; ... } else { croak( "You passed foo() a ", ref($arg), " ref (must be ARRAY or HASH)", ); }

That looks a lot more complex but in reality, it will likely eliminate duplication because your check for "is an ARRAY ref" needs to also be done where you decide what to actually do with the ref and this structure lets you do that check just once. That can also have benefits by keeping the error checking closer to the associated code so that deciding to handle an empty hash is more likely to have the error checking updated to match at the same time.

I'm now considering if I should avoid the subtle 'not' in complex cases by using the low-precendence 'not' as in 'if not ...' to replace the 'unless'. I haven't tried that anywhere yet, so I can't really recommend it yet.

Note that I've seen experienced programmers introduce real bugs that make it to Production because they used 'and', 'or', and/or 'not' in logical expressions (presumably because it "reads better") and were not always aware enough of some of the impacts of the extremely low precedence. So "if( not ... )" would end up being a special-case exception to our rule against using low-precedence logical operators in logical expressions.

- tye        


In reply to Re^6: inheritance: constructors (unless) by tye
in thread inheritance: constructors by Basilides

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others drinking their drinks and smoking their pipes about the Monastery: (2)
    As of 2014-09-15 03:27 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      My favorite cookbook is:










      Results (145 votes), past polls