Style and Efficacy

Programming and programming well are related, but distinct skills. If we only wrote programs once and never had to modify or maintain them, if our programs never had bugs, if we never had to choose between using more memory or taking more time, and if we never had to work with other people, we wouldn't have to worry about how well we program. To program well, you must understand the differences between potential solutions based on specific priorities of time, resources, and future plans.

Writing Perl well means understanding how Perl works. It also means developing a sense of good taste. To develop that skill, you must practice writing and maintaining code and reading good code. There are no shortcuts--but you can improve the effectiveness of your practice by following a few guidelines.

Writing Maintainable Perl

The easier your program is to understand and to modify, the better. This is maintainability. Set aside your current program for six months, then try to fix a bug or add a feature. The more maintainable the code, the less artificial complexity you will encounter making changes.

To write maintainable Perl you must:

Writing Idiomatic Perl

Perl steals ideas from other languages as well as from the wild world outside of programming. Perl tends to claim these ideas by making them Perlish. To write Perl well, you must know how experienced Perl programmers write it.

Writing Effective Perl

Knowing Perl's syntax and semantics is only the beginning. You can only achieve good design if you follow habits to encourage good design.

Exceptions

Programming would be simpler if everything always worked as intended. Unfortunately, files you expect to exist don't. Sometimes you run out of disk space. Your network connection vanishes. The database stops accepting new data.

Exceptional cases happen, and robust software must handle those exceptional conditions. If you can recover, great! If you can't, sometimes the best you can do is retry or at least log all of the relevant information for further debugging. Perl 5 handles exceptional conditions through the use of exceptions: a dynamically-scoped form of control flow that lets you handle errors in the most appropriate place.

Throwing Exceptions

Consider the case where you need to open a file for logging. If you cannot open the file, something has gone wrong. Use die to throw an exception:

    sub open_log_file
    {
        my $name = shift;
        open my $fh, '>>', $name
            or die "Can't open logging file '$name': $!";
        return $fh;
    }

die() sets the global variable $@ to its argument and immediately exits the current function without returning anything. If the calling function does not explicitly handle this exception, the exception will propagate upwards to every caller until something handles the exception or the program exits with an error message.

Catching Exceptions

Uncaught exceptions eventually terminate the program. Sometimes this is useful; a system administration program run from cron (a Unix jobs scheduler) might throw an exception when the error logs have filled; this could page an administrator that something has gone wrong. Yet many other exceptions should not be fatal; good programs can recover from them, or at least save their state and exit more cleanly.

To catch an exception, use the block form of the eval operator:

    # log file may not open
    my $fh = eval { open_log_file( 'monkeytown.log' ) };

As with all blocks, the block argument to eval introduces a new scope. If the file open succeeds, $fh will contain the filehandle. If it fails, $fh will remain undefined, and Perl will move on to the next statement in the program.

If open_log_file() called other functions which called other functions, and if one of those functions threw its own exception, this eval could catch it, if nothing else did. There is no requirement that your exception handlers catch only those exceptions you expect.

To check which exception you've caught (or if you've caught an exception at all), check the value of $@:

    # log file may not open
    my $fh = eval { open_log_file( 'monkeytown.log' ) };

    # caught exception
    if ($@) { ... }

Of course, $@ is a global variable. For optimal safety, localize its value before you attempt to catch an exception:

    local $@;

    # log file may not open
    my $fh = eval { open_log_file( 'monkeytown.log' ) };

    # caught exception
    if ($@) { ... }

You may check the string value of $@ against expected exceptions to see if you can handle the exception or if you should throw it again:

    if (my $exception = $@)
    {
        die $exception unless $exception =~ /^Can't open logging file/;
        $fh = log_to_syslog();
    }

Rethrow an exception by calling die() again, passing $@.

You may find the idea of using regular expressions against the value of $@ distasteful; you can also use an object with die. Admittedly, this is rare. $@ can contain any arbitrary reference, but in practice it seems to be 95% strings and 5% objects.

As an alternative to writing your own exception system, see the CPAN distribution Exception::Class.

Exception Caveats

Using $@ correctly can be tricky; the global nature of the variable leaves it open to several subtle flaws:

Writing a perfectly safe and sane exception handler is difficult. The Try::Tiny distribution from the CPAN is short, easy to install, easy to understand, and very easy to use:

    use Try::Tiny;

    my $fh = try   { open_log_file( 'monkeytown.log' ) }
             catch { ... };

Not only is the syntax somewhat nicer than the Perl 5 default, but the module handles all of those edge cases for you without your knowledge.

Built-in Exceptions

Perl 5 has several exceptional conditions you can catch with an eval block. perldoc perldiag lists them as "trappable fatal errors". Most are syntax errors thrown during compilation. Others are runtime errors. Some of these may be worth catching; syntax errors rarely are. The most interesting or likely exceptions occur for:

If you have enabled fatal lexical warnings (Registering Your Own Warnings), you can catch the exceptions they throw. The same goes for exceptions from autodie (The autodie Pragma).

Pragmas

Perl 5's extension mechanism is modules (Modules). Most modules provide functions to call or they define classes (Moose), but some modules instead influence the behavior of the language itself.

A module which influences the behavior of the compiler is a pragma. By convention, pragmas have lower-case names to differentiate them from other modules. You've heard of some before: strict and warnings, for example.

Pragmas and Scope

A pragma works by exporting specific behavior or information into the enclosing static scope. The scope of a pragma is the same as the scope of a lexical variable. In a way, you can think of lexical variable declaration as a sort of pragma with funny syntax. Pragma scope is clearer with an example:

    {
        # $lexical is not visible; strict is not in effect
        {
            use strict;
            my $lexical = 'available here';
            # $lexical is visible; strict is in effect
            ...
        }
        # $lexical is again not visible; strict is not in effect
    }

Just as lexical declarations affect inner scopes, so do pragmas maintain their effects on inner scopes:

    # file scope
    use strict;

    {
        # inner scope, but strict still in effect
        my $inner = 'another lexical';
        ...
    }

Using Pragmas

Pragmas have the same usage mechanism as modules. As with modules, you may specify the desired version number of the pragma and you may pass a list of arguments to the pragma to control its behavior at a finer level:

    # require variable declarations; prohibit bareword function names
    use strict qw( subs vars );

Within a scope you may disable all or part of a pragma with the no builtin:

    use strict;

    {
        # get ready to manipulate the symbol table
        no strict 'refs';
        ...
    }

Useful Core Pragmas

Perl 5 includes several useful core pragmas:

Several useful pragmas exist on the CPAN as well. Two worth exploring in detail are autobox, which enables object-like behavior for Perl 5's core types (scalars, references, arrays, and hashes) and perl5i, which combines and enables many experimental language extensions into a coherent whole. These two pragmas may not belong yet in your production code without extensive testing and thoughtful consideration, but they demonstrate the power and utility of pragmas.

Perl 5.10.0 added the ability to write your own lexical pragmas in pure Perl code. perldoc perlpragma explains how to do so, while the explanation of $^H in perldoc perlvar explains how the feature works.