Don’t ever not avoid negative logic

If you want to be nice to people with a challenged relationship with boolean logic, try to avoid negative formulations and negations.

I start this post with a confession: I’m not able to discern true from false. I wasn’t born with this inability, it got worse over time. The first time I knew I have this problem was in driver’s school when my teacher told me that most people cannot switch from forward to backward drive and still tell left from right. Left and right are the same to me ever since, even in forward motion. When I was taught boolean logic, my inability spread from “left and right” to “true and false” and led to funny results in some tests, especially multiple choice questions with negative statements. But my guess is that I’m not alone with this problem.

No negations

So I’m probably a little bit over-sensitive about this topic, but that should only make the point clearer: Don’t obscure your (boolean) statement with unnecessary layers of negation. See? I just did it, too. Let me rephrase: Always state your boolean logic without negation, if possible.

It’s really easy for us super-clever programmers to juggle several dozen variables in our head and evaluate any boolean statement on the fly by reading it once – regardless of parenthesis. Well, until it’s not. The thing about boolean logic is that you can’t be “unsure”. It’s only ever “true” or “false”, and just by wild guessing, you will be right about it half the time – try that with basic numerical algebra! So even if the statement looks daunting, you have a fifty-fifty chance of success.

Careful crafting

For me (and probably all people with “boolean disability”, as I call it), every boolean statement is a challenge. So you can be sure that I put maximum effort in succeeding. I write my statements carefully and with great emphasis on clarity (this blog post only covers one aspect). I re-read them several times, sometimes aloud (to my imaginary rubber duck). I thoroughly test them – most statements are factored into their own method to achieve direct testability. And I try them out before committing. Still, there is a valid chance that my boolean disability didn’t magically disappear when I wrote my unit tests and I happily asserted that the statement always has to decide the right things in the wrong way.

By painful introspection about the real nature of my boolean disability, I discovered a great easement: If a statement doesn’t flip everything on its head by negation or negative formulation, I can actually follow through most of the time. Let me rephrase for clarity: If a statement uses negation, it is hard for me to follow. And I guess everyone has a personal limit:

ow_owl

A workaround

The workaround for my boolean disability is really easy: Express the statement like it really was meant in the first place. Express it without “plot twist”. Instead of

if (!string.isEmpty())

try something like

if (string.hasContent())

Disclaimer: I know that the Java SDK (still) doesn’t provide this method. It was just an example.

A real-life example

A real-life example that caused us some troubles can be found in the otherwise excellent Greenmail plugin for Grails. In the configuration, you can set the property

greenmail.disabled = true

to disable the mail server that otherwise would start automatically. The positive formulation would be

greenmail.enabled = false

To tell the full story: The negated formulation was probably chosen to simplify the plugin’s implementation in Groovy. The side effect of this short-cut is that you can’t state

greenmail.disabled = false

and be sure that it will start the mail server. In fact, it won’t. As a developer challenged by boolean logic, this issue gave me nightmares.

The three-state trap

Using this rule as a guideline for boolean statements will also prohibit that you fall into the “three-state trap”. Imagine a Person object with the method

boolean isOlderThan(Person other)

But you want to know if a person is younger than another, so you just negate the result:

if (!personA.isOlderThan(personB))

just to be clear, following the rule of “no negations”, you would’ve written:

if (personA.isYoungerThan(personB))

which isn’t quite the same! If both persons are of equal age (the “third state”), the negated statement returns true (if I evaluated it correct!), whereas the last statement gives the correct answer (false – not younger).

Use as a guideline

Don’t get me wrong: Avoiding negations isn’t always possible or the best available option. This isn’t a law, it’s a guideline or a rule of thumb. And just because some complex boolean statement is free of negations doesn’t make it acceptable automatically. It’s just a tiny step towards pain-free boolean statements. And that’s a bad thing… NOT.

9 thoughts on “Don’t ever not avoid negative logic”

  1. Your plot twist analogy is kind of flawed. I agree, that for specific cases one should go for the “most common” boolean expression that evaluates to true, however, just because you need the negation of “is empty” in a particular case does not mean that you never have to check for the truth. Let’s suppose Java has string.hasContent() and now you want to check if the string is empty …

    1. The “plot twist” metaphor describes how an unexpected negation feels for me. And of course, string.hasContent() alone isn’t helpful, string.isEmpty() is still needed. In my age-of-person example, three methods are required: isYounger(), isOlder(), isOfEqualAge().
      In application development (opposed to API design), where the foreseeable use case is known, the logic should be expressable in positive terms.

  2. As a beginner programmer, in my desperation to avoid negative logic I embarrassingly write statements like:
    if allowStuff then
    ‘ do nothing
    else
    {code to prevent STUFF from happening}
    end if
    I felt stupid doing that until I read this article 🙂

  3. With negation “!”, given only a few minimal operators, the others can be expressed in terms of them.

    (Examples:

    For two scalars “a” and “b”: if “a == b” is defined, then:

    – “a != b” is “!(a == b)” [extract the negation]

    For two numbers “x” and “y”: if “x y” is “y = y” is “!(x < y)" [by exclusion]
    – "x <= y" is "!(y y)” [exclusion] then reverse, or “y >= x” [reverse] then exclusion]

    For two Booleans “p” and “q”: if “p && q” is defined, then:

    – “p || q” is “!(!p && !q)” [via “!!(p || q)” [double negation] i.e. “!(!(p || q))” then apply De Morgan’s law]

    )

    But this works in the opposite way too, so generally you can reduce (if not remove) the use of negation by using another operator!

  4. _(Note: this is a re-post tentative of my original comment but with angle brackets HTML-encoded…)_

    With negation “!”, given only a few minimal operators, the others can be expressed in terms of them.

    (Examples:

    For two scalars “a” and “b”: if “a == b” is defined, then:

    – “a != b” is “!(a == b)” [extract the negation]

    For two numbers “x” and “y”: if “x < y” is defined, then:

    – “x > y” is “y < x” [reverse the direction] (well, actually this one doesn’t need negation…)
    – “x >= y” is “!(x < y)” [by exclusion]
    – “x <= y” is “!(y < x)” [via either “!(x > y)” [exclusion] then reverse, or “y >= x” [reverse] then exclusion]

    For two Booleans “p” and “q”: if “p && q” is defined, then:

    – “p || q” is “!(!p && !q)” [via “!!(p || q)” [double negation] i.e. “!(!(p || q))” then apply De Morgan’s law]

    )

    But this works in the opposite way too, so generally you can reduce (if not remove) the use of negation by using another operator!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.