From Novice to Adept: Declarations and Scope

| 2 Comments

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.

2 Comments

All my scripts have this:


sub main {
# ...
}
exit(main());

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!

sub find_customers
{
    my $dbh = shift;
    my $customers = $dbh->selectall_arrayref( 'SELECT ...' ) || [];
    return @$customers;
}
or even
sub find_customers
{
    return @{ shift->selectall_arrayref( 'SELECT ...' ) || [] };
}

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on October 15, 2009 2:08 PM.

From Novice to Adept: Scalar Context and Arrays was the previous entry in this blog.

From Novice to Adept: Cleaning Up Bad Code is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?