Guideline VR2: Variable Forgiveness
Forgiveness is a wonderful thing... until it comes to programming languages. Forgiveness in relation to a programming language is another word for sloppiness. Some languages allow more sloppiness than others, though some might say this provides for more freedom of expression. Phooey! Express yourself in your code design, not in slipshod use of the language.
First a word on proper terminology. A declaration describes the form of an variable. A definition reserves the actual space used. And, an initialization assigns a value. These three steps are separable in some languages, completely intertwined in others. My example below is loosely based on an example from Sun's Java tutorial on language basics, which provides a clear distinction between these three:
public class CreationDemo { public static void main(String[] args) { int[] myArray; // array declaration // System.out.println("Array after declaration: " + myArray); // illegal myArray = new int[5]; // an array definition System.out.println("Array after definition: " + myArray); System.out.println("Last array cell: " + myArray[myArray.length-1]); for (int index = 0; index < myArray.length; index++) { myArray[index] = index; // array initialization } System.out.println("Array after initialization: " + myArray); System.out.println("Last array cell: " + myArray[myArray.length-1]); } }
Array after definition: [I@ba34f2 Last array cell: 0 Array after initialization: [I@ba34f2 Last array cell: 4
The output of the above program should be something like that shown. Note that after declaration we cannot refer to the array at all--it will produce a compiler-time error, because all we have done to that point is announce the form of the variable, nothing more. Once we define it, you can see that it takes up space; the print statement shows its memory address. The keen observer will wonder why the next print statement is legal; after all, we have only created space, we have not yet put anything in that space (initialization). True, but Java helps us out by initializing certain entities, including integer arrays to zero automatically. (See Jane Griscti's well-builtJava reference for a great summary of Java's habits.) Once we do initialize the array, then we can retrieve a value from it as shown in the last statement.
my @myArray; # declaration @myArray = (1, 2, 3); # definition and initialization my $myScalar; # declaration $myScalar = "stuff"; # definition and initialization
Perl, by contrast to Java, has declarations, but it does not distinguish between definition and initialization, as shown.
Perl, too, has the deficiency that variables do not have to be declared! You may choose not to use this deficiency by always using the use strict
pragma in your programs--obviously necessary for clean code.
int x; // declaration x = 25; // definition and initialization // array: combining declaration and definition int[] array1 = new int[5]; // array: combining definition and initialization array2 = new String[] { "abc", "def", "ghi" }; // combining declaration, definition, and initialization int y = 25; int[] array3 = { 1, 2, 3, 4, 5 };
Combining definition and initialization is also available in Java, and in many other languages. Indeed, it is the norm when dealing with non-array types. For arrays, it is common to combine definition and initialization, or declaration and definition.
And, many languages provide the concise ability to combine declaration, definition, and initialization in a single statement. If you know the contents at the time you're ready to create a variable, clean code dictates you do this combination if the language allows it. In the example below, the first part will work, but it is error prone because it duplicates code.
The first statement is a declaration and definition for 5 elements. The second statement is a re-definition for 5 elements. If they disagree on number, that will work just fine--but it is doubly-wrong! Because this code fragment is also duplicating memory allocation. The initially allocated storage for 5 elements is immediately discarded without ever being used; the second statement allocates a new chunk of space, fills it with the given values, and assigns it to myArray
.
int[] myArray = new int[5]; myArray = new int[] { 1, 2, 3, 4, 5 };
int[] myArray = { 1, 2, 3, 4, 5 };
JavaScript is the worst offender when it comes to implicit variable declarations. You are not required to declare variables, either globally or locally. You may just start using them willy-nilly. Unlike Perl, there is no way to get around this pitfall other than diligence.
This leads to tremendous potential for error.
For one thing, as discussed in VR1,
if you omit the declaration, you are automatically creating a global variable, even though you might think you are declaring a local variable.
And just to be clear, count = 5;
at the beginning of your JavaScript program is not a declaration; that is a definition.
On the other hand, var count = 5;
would be a proper declaration.
The example below is clean code: all variables are declared before use, and all variables are defined before use.
If you run this code, you'll get a report of 2 animalia and 1 monera.
If this was completely sloppy code--remove all the variable declarations (i.e. the var
keyword)--you would get a very different result.
Remarkably, you can change the sloppy, wrong code to sloppy, correct code by keeping just one var, highlighted inside the catalog
function.
But it will still be sloppy, subject to future pitfalls.
var species = [
{ name: "kookaburra", kingdom: "Animalia" },
{ name: "tawny frogmouth", kingdom: "Animalia" },
{ name: "gooey stuff", kingdom: "Monera" }
];
var NUM_SPECIES = species.length;
var kingdoms = [
{ name: "Animalia", count: 0 },
{ name: "Plantae", count: 0 },
{ name: "Fungi", count: 0 },
{ name: "Protista", count: 0 },
{ name: "Monera", count: 0 }
];
var NUM_KINGDOMS = kingdoms.length;
var index = 0;
while (index < NUM_SPECIES) {
catalog(species[index++]);
}
function catalog(species) {
for (var index = 0; index < NUM_KINGDOMS; index++) {
if (species.kingdom == kingdoms[index].name) {
kingdoms[index].count++;
}
}
}
var s = "";
for (var index = 0; index < NUM_KINGDOMS; index++) {
s += kingdoms[index].name+": "+ kingdoms[index].count + "\n";
}
alert(s);
var x; // declaration* x = 25; // definition and initialization var array1; // declaration* var array2 = new Array(5); // declaration* and definition var array3 = [ 1,2,3,4,5 ]; // declaration*, definition, initialization array4 = [ 1,2,3,4,5 ]; // ??
JavaScript is a weakly-typed language (see VR3 for more); it does not specify the types of variables during declaration. That is why I denote it as "declaration*" in the sample. JavaScript does offer the flexibility of variable creation as shown with array1
, array2
, and array3
, but what exactly is array4
?
The answer is, "It depends." If array4
has previously been declared, then this statement is definition and initialization. If it has not previously been declared, then this statement is declaration, definition and initialization.