[Chapter 5] 5.2 The Block Form: Exception Handling
Author: Sriram Srinivasan
Date: 1999-07-02T01:32:35Z

#

| Chapter 5 Eval |

## 5.2 The Block Form: Exception Handling

In this form, `eval` is followed by a block of code, not a scalar containing a string. It is used for handling run-time errors, or exceptions. Errors can be internal built-in ones (out-of-memory, divide-by-zero) or user-defined ones produced by `die`.

The following example shows how you can use the block form `eval` to trap a run-time divide-by-zero error:

    eval {
         $a = 10; $b = 0;
         $c = $a / $b;     # Causes a run-time error,
                           # which is trapped by eval
    };
    print $@;   # Prints  "Illegal division by 0 at try.pl line 3

When the script is compiled, Perl syntax-checks the block of code and generates code. If it encounters a run-time error, Perl skips the rest of the `eval` block and sets `$@` to the corresponding error text.

To signal your own errors, you use `die`. Perl knows whether a piece of code is currently executing inside an `eval`, and so, when `die` is called, Perl simply gives the error string - `die`'s argument - to the global `$@`, and jumps to the statement following the `eval` block. In the following example, `open_file` invokes `die` if it has trouble opening a file. To use this function, wrap it inside an eval.

    sub open_file {
        open (F, $_[0]) || die "Could not open file: $!";
    }

    $f = 'test.dat';
    while (1) {
        eval {
              open_file($f);   # if open_file dies, the program doesn't quit
        };
        last unless $@;     # no error: break out of the loop.
        print "$f is not present. Please enter new file name $f";
        chomp($f = <STDIN>);
    }

Java/C++ programmers would of course recognize the parallel to the throw, try, and catch statements, where try corresponds to the `eval` block, catch to the checking of `$@`, and throw to `die`. (Essentially, the caller says to the run-time environment, "Here, try this code, and catch whatever errors are thrown by the callee.")

One thing I like a lot about the Java environment is that both the interpreter and the standard libraries make extensive and consistent use of try, throw, and catch for error handling. In some ways, using these constructs is better than simply returning an error code, because it requires the programmer to pay attention to errors (if you ignore the error, the program dies).

In C++ and Java, a function can rethrow an exception if it doesn't want to handle it itself. In Perl, you can do so by calling `die` without arguments:

    eval {
        ...
    };
    if ($@ =~ /sorry, bucko/) {
        ....
    } else {
       # hmm .. don't know what to do with it.
       die;  # Identical to die $@
    }

If there is an enclosing `eval` block, this exception will be caught; otherwise, the program terminates.

### 5.2.1 Standard Modules

Since C++ and Java contain special constructs for trapping and handling errors, some Perl programmers would like them too. Here are a couple of options.

#### 5.2.1.1 Exception.pm

As this book goes to press, a new module, Exception, built over `eval` and `die`, is just being announced to CPAN. You need to understand Perl's support for object orientation to understand the following small example, so you might want to revisit this example on a subsequent reading.

This snippet throws exceptions if you attempt to withdraw more than $300 or exceed the current balance:

    use Exception;
    package AmountExceededException; # User-defined exception
    @ISA = ('Exception');

    package OverdraftException;      # User-defined exception
    @ISA = ('Exception');

    package BankAccount;
    sub withdraw_money {
        my $amount = shift;
        if ($amount > 300) {
            throw new AmountExceededException;
        }
        if ($amount > $balance) {
             throw new OverdraftException;
        }
        ...    # Change balance
    }

    try {
        print "How much do you need?"; chomp($amount = <STDIN>);
        withdraw_money ($amount);
    }
    catch AmountExceededException =>
                   sub {print 'Cannot withdraw more than $300'},
          OverdraftException      =>
                   sub {print $_[0]->message},
          Default =>
                   sub {print "Internal error. Try later"};

#### 5.2.1.2 exceptions.pl

The standard Perl library currently has a module called exceptions.pl, which is also a thin wrapper over `eval` and `die` and provides subroutines called `catch` and `throw`. `catch` takes a piece of code as a string (instead of as a block, as the previous example) and a list of regular expressions to match against the error string when it `eval`'s the code.

This module has one serious problem, which is actually solved by the newer module, Exception.pm: because `catch` is a subroutine, lexical variables in the current scope (localized with `my`) are not available to it.

I suspect that programmers disdain making a language look like another;[3] in the final analysis, using `eval` and `die` in the raw is probably the easiest option.

> [3] That excludes Larry Wall, considering that he designed Perl to look like C, sh, and awk!

5.1 The String Form: Expression Evaluation |  | 5.3 Watch Your Quotes