Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

Re^2: Burned by precedence rules

by GrandFather (Sage)
on Dec 26, 2008 at 04:50 UTC ( #732631=note: print w/replies, xml ) Need Help??

in reply to Re: Burned by precedence rules
in thread Burned by precedence rules

Many would find that better expressed as:

sub done { my $self = shift; return 1 unless $self->foo or $self->bar; return; }

because it avoids the two negations which confuse the meaning of the and.

I used to avoid using unless, partly because none of the other languages I've used have had such a thing, but where it drops out a layer of negation I find it helps clean up intent quite nicely.

Perl's payment curve coincides with its learning curve.

Replies are listed 'Best First'.
Re^3: Burned by precedence rules
by tilly (Archbishop) on Dec 26, 2008 at 05:00 UTC
    I disagree strongly with many people then.

    When we are debugging we do not reliably do De Morgan's laws in our head. I heard this a long time ago and did not believe it until one particularly frustrating debugging session. Ever since then I've avoided using unless with any kind of logic at all no matter how nicely it reads while I'm coding.

      I absolutly agree that unless often becomes very confusing with more than one element in the condition and almost always use if in that case.

      I'm in (southern hemisphere) holiday mode at the moment so my head isn't fully in Perl space. I focused on the two nots rather than the 'and' and actually in the specific case given I think the result conveys the intent better than the if version - no De Morganizing required.

      Perl's payment curve coincides with its learning curve.
        Yes, it reads nicely. But if you're debugging and know the two values, you may be very surprised at what it does. By contrast in that mode you will reliably figure out what the if version does.

        If you can easily run the code this is not so bad, But in my case I was debugging a failure in a batch job that failed after hours of work. In code I hadn't touched in months. Hence my failure to correctly figure out whether the conditional was executed lead to enough pain that I learned my lesson. The if version may not be pretty, but it is just as understandable when coding and a heck of a lot easier to debug. So use it.

      While I understand your point and would not dream of trying to convince you to change, I usually try to use unless in very specific contexts. If there is only one condition and it would normally be expressed as a negative, I find unless {cond} much cleaner than if !{cond}.

      I can understand the point about people not being able to DeMorganize in their heads. (I knew a really, really good programmer once who regularly had problems with and and or) But, I have found that sometimes unless is clearer (just like in English.)

      G. Wade

        Actually, I've seen the confusion happen with just a simple "unless $bool". Making the negation more explicit tends to help. I've quite recently seen several cases where I and people I've worked with were persistently confused as to how code could be doing what it was doing and it turned out that the part that was confusing us was the use of "unless".

        I've also tried to simply replace an "unless" with an "if" and thus produced a horribly complicated bunch of negations (which I didn't keep). Most of the time, I can find other subtle negations and cancel them out. Things like "unless $count != 0".

        Of course, "unless" seems to be "obviously" the same as "if not". But if the human mind actually worked that way, then there would have been no confusion when I answered "Is this preview button regarding updates not wanted?" with "No". The human mind doesn't do "double negatives" like some claim English does (and thus also not like programming languages do).

        So reduce the number of negations in your programming constructs. And make the negations that you do more explicit. "unless" may be appealing because it eliminates a negation, but it doesn't, it just makes the negation more subtle.

        An example of a really bad subtle double negative is "last unless ...". "last" has a subtle negation in it, since it means "do not keep looping".

        And when a conditional gets complicated, sometimes the best route is to just add one or more named items for different parts of the conditional.

        my $is_logged_in= $user && $user->id() != User->anonymous_id(); my $has_guest_coupon= $cookie && $cookie->is_for_guest() && ! $cookie->is_expired(); if( $is_logged_in || $has_guest_coupon ) {

        or, usually better, especially if the short-circuiting is important:

        if( is_logged_in($user) || has_guest_coupon($cookie) ) {

        - tye        

        In that situation I use unless as well. But I flip to if when I need to add and or or.
Re^3: Burned by precedence rules
by Marshall (Abbot) on Dec 26, 2008 at 18:45 UTC

    I think the precedence thing has been covered, but since DeMorgan's and "unless" got mentioned, a few thoughts about:

    1. reducing Not's - this is a good idea!
    2. DeMorgan's and using pictures instead of equations
    3. Table driven approaches

    I agree that factoring out negations improves readability. If you get too many "not's" in there it can get confusing.
    Without the "unless", this is another way:

    sub done { my $self = shift; return !($self->foo or $self->bar); }

    Read as: I'm not done if I've got some foo or bar. To me this is easier than I'm done if I don't have this and I don't have that, but situations vary.

    As far as converting between "AND's" and "OR's", I always draw a picture, not a table or an equation - just some little doodle on a piece of scratch paper.

    When looking at (!$self->foo && !$self->bar), I would draw an AND gate with two bubbles on the inputs, interpreted as both low's on input means a high output, then by inspection see that this means that any high input yields a low output. Well, that is a NOR gate. So I would draw an OR gate with a bubble on the output. Then I would compare pictures and verify that they really do the same thing.

    So instead of working with some De Morgan's equation, work with the picture. Ask a hardware guy to show you how they do it. 30 minutes learning how to draw and interpret these pictures (simple logic circuit diagrams) will save you a lot of grief.

    If things get too hairy to draw a simple picture, then simplify things. Maybe you combine some things together in an intermediate expression which is used in the following statement, or something like that...

    I guess I'm getting far afield, but there is another technique when things get REALLY hairy, the table driven approach. This is something to consider with "monster if's"... Depending upon the situation, it might or might not work out to be easier to understand and maintain.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://732631]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (9)
As of 2018-06-20 15:39 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (116 votes). Check out past polls.