Thursday, March 14, 2013

Parenthesis, just for emphasis

Working in a certain language for a number of years has its benefits.  The longer you work with a language the more familiar you become with the nuances and syntax.  Less time is spent on Google learning about the libraries or why one method of doing something is more efficient than another.

I have been working for the past few years doing ASP.NET development with C# as the language of choice.  While I do plenty of C# programming, I also write a lot of HTML and JavaScript.  C# is a strongly typed language while JavaScript is not.  C# is compiled while JavaScript is interpreted.

Over the years I have been careful to keep these things in mind as I program because subtle programming errors that are usually prevented because a language is strongly typed or because the compiler caught me being stupid can cause nightmares and wreak havoc in those languages that are not.

A good example of this is when assignment was used where equality was desired.

In C# we have a compiler that will slap us on the hands for this:
public static void Main(){
 int i = 10; 
 int b = 20;

 if(a = b) //compiler warning
 {
  //assignment always happens
 }
 else
 {
  //dead code
 }
}
But, in JavaScript we don't have a compiler to nag at us for this:
function Main(){
 var i = 10; 
 var b = 20;

 if(a = b) //no compiler so, no warning
 {
  //assignment always happens
 }
 else
 {
  //dead code
 }
};

Well, recently I was bit by just this very type of issue with a twist.  I had some code that was using an integer flag to represent the valid values of an instance variable (see Flag Word).  In my JavaScript code I had de-serialized a value from the server that was supposed to represent the style of an HTML element.  The possible values were:
  • Bold
  • Underline
  • Italic
I had an enumeration that emulated the enumeration on the server and all that was needed was to determine if I should apply the styles to the element.  Easy enough, I just needed to make a bit wise comparison and check the result (see Querying the status of a bit).  So off I went.
function ApplyStyles() {
 var deserializedValue = 7;

 var styleMask = {
  Bold: 1,
  Underline: 2,
  Italic: 4
 }

 if (deserializedValue & styleMask.Underline == styleMask.Underline) {
  //execute because 7 & 2 == 2 should evaluate to true
 }
}
But, the if block never got executed.  Why not?  I set a break point because I figured the values that were being returned from the server were not what I was expecting.  What I found was exactly what I expected.  The values in the watch window looked like the comment in the above code.

So, I called a co-worker over.  He looked at the watch window and scratched his head.  After a bit of poking around in the debugger he asked me to evaluate both sides of the bit-wise and operator (&).  the left side was 7 and the right side was True.  Doh! we added an extra set of parentheses and it worked.
function ApplyStyles() {
 var deserializedValue = 7;

 var styleMask = {
  Bold: 1,
  Underline: 2,
  Italic: 4
 }

 if ((deserializedValue & styleMask.Underline) == styleMask.Underline) {
  //extra parentheses (7 & 2) == 2 NOW its true
 }
}
Why, what happened? "I do this all the time in my C# and don't need the parentheses... why do I need them here." I ranted.  So to prove it to myself,  off I went and looked at my C# code. OMG there they were, an extra set of parentheses . Why!!! Whats up with that?

We need to go back to our intro Computer Science classes and remember operator precedence (see Order of operations - Programming languages).  The bit-wise AND and bit-wise OR operators are of lower operator precedence than equality.  What the hell, I thought they were evaluated before equality, somewhere around the bit-wise shift operators.

Well here is where working in a strongly typed language and relying on the debugger has made me lazy and forgetful.  In C# the compiler nags when you attempt to do this.  You will get a compiler warning.
Operator '&' cannot be applied to operands of type 'int' and 'bool' 
It all makes perfect sense, I have conditioned myself when doing this type of operation.  I have conditioned myself poorly.  I have waited until the compiler identified my bad code and then I would go back and correct it instead of writing better code to being with.

Shame on me!

No comments:

Post a Comment