I've read a lot of novice Perl code over the past decade, often in response to requests for help.
One common feature of novice code is file-scoped variables:
#! perl
use strict;
use warnings;
my @customers; # array of customers
my $sth; # database statement handle
my $i; # index variable
# ...
my %records; # hash of records
You're lucky if the code is this good; often it's face-palmingly worse, as in the case of Microsoft's awful Perl code for the 2008 Winter Scripting Games:
%dictionary = (); # create a hash table
The problem with the big block of variable declarations is that it fails to take advantage of the notion of scope -- that you can draw a little box around a unit of code and keep certain pieces of information inside that block private to the block.
If I went to my fridge and took out the leftover pizza you were saving in your fridge for lunch, you'd be confused and upset by our haunted magic refrigerators. Similarly (if less gastronomically pleasing to me), modifying a global variable somewhere where you didn't expect it to have an effect elsewhere is confusing and frustrating.
While advocates of certain dynamic languages decry the use of variable declarations as a relic of stupid static typing systems like you might find in the C, C++, and Java languages, Perl variable declarations have an enormous benefit in clarity: they help you see the scope of variables.
Yes, this means that sometimes adding syntax to a language can improve clarity.
There's a huge difference between:
my @customers; # array of customers
my $dbh; # database handle
my $sth; # statement handle
...
find_customers();
sub find_customers
{
$sth = $dbh->prepare( 'SELECT ...' );
while (my $row = $sth->fetchrow_arrayref())
{
push @customers, $row;
}
}
... and:
my $dbh; # database handle
...
my @customers = find_customers($dbh);
sub find_customers
{
my $dbh = shift;
my $sth = $dbh->prepare( 'SELECT ...' );
my @customers;
while (my $row = $sth->fetchrow_arrayref())
{
push @customers, $row;
}
return @customers;
}
The second code may look more complex, but it has several advantages:
find_customers()
can modify the contents of the$dbh
and@customers
variables internally without affecting code elsewhere.- The
$sth
variable does not conflict with other statement handles elsewhere. As well, Perl will clean it up when the function returns. find_customers()
can work with several different database handles, if necessary: the code is more reusable.- The inputs and outputs to the function are clear: every variable used within the function has a clearly scoped lifetime.
This issue may seem like a minor quibble over style -- after all, in small programs, scope really doesn't matter -- but for programs over a few dozen lines, the decrease in the risk of error and improvement in maintainability and readability is substantial.
All my scripts have this:
So I never declare any variables outside of a function. Constants go outside functions but those get Readonly'd and scoping isn't a big issue with constants. This approach takes care of the global issue and ensures that all the interesting bits have a nice left margin; code right up against the left side of my editor hurts my eyes, I blame C for that. I remember being taught a similar thing in university when I was learning Pascal, they called it the "global filter" at the time.
[Sigh] I know this is not related to the point of your post, but I do wish people would stop writing 20th century DBI code. The selectall_arrayref() method was added in 1998!
or even