Wednesday, April 15, 2009

Constants help make extended rules self-documenting

Self-documenting code: what a concept! Isn't it wonderful when you sit down at somebody else's code and you find that their use of variable names, constant definitions, and function names self-document what's going on - and in fact, do it so well that comments are hardly needed.

The map editor lets you define a table of constants. Most of us may think this is limited to string constants, but not so: it works perfectly well for integer and floating point constants too.

In fact, I strongly recommend and prefer that standard integer constants always be defined here. I had some problems in the past with integer literals in extended rules being compiled as floating point and being used as such at run time. You know, like a trivial expression like i = i + 1; has the 1 in floating point at run time. This causes the i, even though it is an integer, to be converted to real, the math performed, and then the final result converted back to integer. While I haven't tested this phenomenon with recent versions of the software, I do habitually define common integer constants as constants.

The same also holds true with boolean variables. There are no boolean types in the extended rules, so we must use integers. Typically we use 0 and 1: but why not simply define true and false as integer constants?

So, when I create a map, any map, I always define these integer constants:
  • true = 1
  • false = 0
  • zero = 0
  • one = 1
  • inc = 1
  • p0 = 0; that is, p-zero
  • p1 = 1
  • p2 = 2
  • ...
  • p9 = 9
  • document_grplvl = 0
I always define the single-digit integers from 0 - 9 as constants as they are commonly used, particularly 0 and 1.

One might ask why zero and p0? And for that matter, why inc and p1? This has to do with coding philosophy as it is meant to document the reason why you are using 0 and 1. p1 is simply a constant 1 with no meaning associated with it. inc on the other hand is specifically used for incrementing, such as through an array. Like in C++ or Java, we use i++, and not i = i + 1.

When I am iterating through an array, with a zero-based index, I would code something like this:

integer max_arr, i;

max_arr = 10;
idx = zero;

while idx < max_arr do begin
// yaddy yaddy ya
idx = idx + inc;
end

Notice how the iterator stays in pure integer form. Notice how the use of inc on the last line makes it self-evident to the reader that you are incrementing the iterator.

Likewise with boolean, use a variable name that implies its purpose:

integer has_stateProv;
string[2] stateProv;

stateProv = #0156;
if stateProv = "" then
has_stateProv = false;
else
has_stateProv = true;

// some N4 stuff here

if has_stateProv = true & has_country = false then begin
// go figure out the country code
end

I implied the has_country variable, but it would use the same rule, tested from #0026 (N404). Notice how we first capture stateProv from #0156. This is much plainer to the reader than having to remember that #0156 is the state/province code. (Yes, I know all us EDI geeks "know" that, but it is still easier to read.) Isolating your booleans also makes your final compound if statement much more clear, as it pre-cooks the conditions down to simple booleans.

Now what's this document_grplvl all about? That is a separate article in and of itself. But trust me, it is the root of a truly gorgeous concept.

1 comment: