2

I was working on a piece of code when I noticed that an if statement could work or crash depending on the order used for the parts connected with and.

You can replicate the problem like this:

boolean func(String x) {
    assert x
    return true
}
v = "text"

if (v != "" && func(v)) {
   println("Hello")
}

Depending on whether v != "" or func(v) is used first, the if causes an assertion error or not.

This is because groovy checks the first expression and if that one is already false it does not even evaluate the second as the and operator will always cause the expression to be false.

So in groovy the connected condition are combined like this:

if (v != "") {
    if (func(v)) {
        *[...]*
    }
}

I know that at least some other languages have a similar behavior (I believe in Java you can switch it by using & or &&) but I ask myself is this really the way to go. It seems quite unintuitive to understand and I don't think it conveys the importance of the order in this statement.

How can I make it clear on a code basis that the order is important and should be considered when altering code around it? Or should I just forget about it and assume that people check for something like that?


Upon doing further research I discovered an entry on the basic groovy documentation site for operators: Groovy - Short Circuiting

The general term seems to describe this seems to be 'short-circuit evaluation'. Very appropriate. I adapted the title of the question as I wanted to put it in there but didn't know the terminology.

AK_is_curious
  • 286
  • 1
  • 7
  • 5
    Your fellow programmers who are adept at the programming language you are using *already understand* that "order is important" in this context. – Robert Harvey Dec 05 '17 at 17:44
  • 3
    The "short-circuit" `&& or ||` is a very common idiom and it should be clear to other coders. The non-short-circuit `&` ("long circuits?") are unusual and you might want to comment why you want to force evaluation of both sides. – user949300 Dec 05 '17 at 19:50
  • The answer is in your own question: you highlighted the importance of order by nesting conditions. – mouviciel Dec 07 '17 at 09:42

2 Answers2

6

Why ?

In C, C++, Python, Java, C#, Groowy, and Swift (historical order of appearance), and certainly in many other languages as well, the usual logical and-operator (mostly &&) works with short-circuits.

In some languages such as ADA (i.e. and), the short-circuit form requires an extra effort (i.e. and then, which gives a little look like your successively embedded if-statements).

The reason of this behavior is... short circuit, to avoid fatal failures without a lots of additional statements. Language designers have carefully evaluated the pros and cons:

Expressions connected bu && and || are evaluated left to right, and evaluation stops as soon as the truth or falsehood of the result is known. These properties are critical for writing programs that work.
- B.W.Kernighan & D.Ritchie, 1978, The C programming language, P.38

For example, the follwing statement works in all cases :

int a=10, b=0; 
if (b!=0 && a/b>3)   // ok
    ...

If the and connector would be without short circuit, such precautions would be useless: both terms would be evaluated and the a/b would trigger a fatal divide by 0 exception...

if (b!=0 & a/b>3)   // ouch !!! 
    ...

Do you need to worry ?

The world is made of two kinds of programmers: those who are aware of the short-circuit and the order, and those who are not yet.

So don't worry, most of your peers will take care of the order. If you fear that someone might miss something important, just put a comment :

if (b!=0 && a/b>3)   // order avoids divide by zero
    ...
Christophe
  • 74,672
  • 10
  • 115
  • 187
3

&& and || at least in java (and I guess in groovy as well) mean that the operation is short-circuited, i.e. as you said the right hand side will be evaluated if and only if it's necessary.

& and | evaluate both sides.

So if you're using && or ||, it already means that the order is important. In your example, using && implies that func(v) can be evaluated only if v != "" is true.

Vadim Samokhin
  • 2,158
  • 1
  • 12
  • 17
  • It _really_ needs to be noted that both sides are evaluated because it is a bitwise operation, not a logical operator when using booleans. so `1 | 0` evaluates to `1` which is only then treated _logically_ as `True`. `1 || 0` on the other hand is treated as `True ∧ False` which evaluates to `True` (and yes, short circuits as soon as the answer is known) – Baldrickk Jan 15 '18 at 18:29