14 February 2010

The '!' logical-complement operator (fixed)

It's high time to clear doubts about: The '!' logical-complement operator. With little help of someone who can express himself more clearly, subject should be easier to follow.


Some time ago I used to know all operators priorities in C++, using a minimal number of braces resulted with a quite compact code in logical cases. It was really easy to read that again, but only for me, what I did not see as a problem. When I started to work on the same code with others there were no turn back and I had to make conditions more readable...


Languages and braces

Analysing expression with braces requires to locate them as matching pairs, what takes more effort than a simple sequent reading like I used to do before. But the real breakthrough happened when I had to work on two totally different languages in the same time. Since then, big difference in semantics and operator priorities has changed a lot the way I'm wring code now. If someone has doubts whether a similar situation can happen or not, let's consider quite simple expression like: '2+2*2', what gives '6' in Java and '8' in MUMPS . So finally, I ended using a few braces. When is it essential to use braces?
I suggest doing it in order:

  • not to care about operator priorities
  • to prevent situation when modification can have impact on current expression meaning


Practise

Notice that adding braces, when it's necessary, might effect in a hard to analyse code, like having 3 and more closing bracket in a row, what's really confusing. Study this example to see how above rules apply:
Let's assume we have some variables:

boolean main = ...;

boolean current = ...;

boolean additional = ...;
And now code:
if ( main == !current )
becomes improper. Why? Because when we modify it and add something like this:
if ( main == !current && additional )
the code looks a little bit unclear. Why? It happens because we are not sure if negation applies only to variable 'current' or for a whole expression 'current && additional'. In order to prevent from such situation we will apply above rules for using braces. Our expression will be transformed into this:
if ( main == (!current) )
Does this look a little awkward? However, when we look closer at those three expressions below, 2nd and 3rd ones are totally clear, no matter what formatting is used. What's more a modification of our 'if' is easier because it does not require so much cursor travel (to add bracket).
1.
if ( main == !current && additional )
if ( main == ! current&&additional )
2.
if ( main == (!current) && additional )
if ( main == (! current)&&additional )
3.
if ( main == (!(current && additional)) )
if ( main == (! (current&&additional)) )

So let's see how useful it will be when we exchange 'current' with little wider XML-like expression chain:

data.getAccountData().getUnit().getContract().isOpen()
we get 'if' looking like:
if (main == data.getAccountData().getUnit().getContract().isOpen() && additional)
Now if we want to negate isOpen() part in this expression, we write something like this:
if (main == (!data.getAccountData().getUnit().getContract().isOpen()) && additional)
while I would suggest:
if (main == data.getAccountData().getUnit().getContract().!isOpen() && additional)


instanceof operator

The next example is instanceof operator and it's negation. Now, in order to negate instanceof we must write:

if (!(obj instanceof Person)){...}
While I would suggest:
if (obj !instanceof Person){...}


Of course those examples require a few analysis, as a behaviour for null-s. But it's not a big deal in this case.


Work around:

We can generate isOpen() as well as isNotOpen() instead of one method in that case. But it is rather a naive solution - isNotOpen() version in most cases calls traditional isOpen() with simple negation, what increases stack trace. Another problem could also occur during a code fixing, because someone could repair only one method (if both would contains implementations).


2 comments:

  1. Hi,
    very interesting post, but I have a few suggestions.

    to: "if ( main == !current && additional )'"
    you should write something like that 'if (!current && additional == main), it is more simple because is similar to compiler order interpreting

    to: "Work around: We can generate isOpen() as well as isNotOpen() instead of one method in that case."
    creating two method isOpen() na isNotOpen() is bad. When we create two method we have to succeses: isOpen() nad !isNotOpen(), and we have two failures: !isOpen and isNotOpen. Now our code is 2 time more complicated and we must remember: the human brain prefers resolve condition at 'true'

    ReplyDelete
  2. @arkadiusz borek

    I care about compiler as little as possible.

    About isOpen/isNotOpen I have exact same opinion as you, although some times it still can be good solution.

    ReplyDelete