Previous Page
Next Page

13.3. Contextual Failure

Make failures fatal in all contexts.

The Fatal pragma can also be invoked with the special marker :void. Loading Fatal with this extra marker causes it to rewrite builtins and subroutines in a slightly different way, such that they throw a failure exception only if they were called in a void context. Under :void, they continue to silently return false in non-void contexts. That is:

    use Fatal qw( :void open close );

    if (open my $out, '>', $filename) {         # Call to open(  ) in non-void context so
                                                #     open(  ) returns false on failure

        open my $in, '<', '$filename.dat';      # Call to open(  ) in void context so
                                                #     open(  ) throws exception on failure

        print {$out} <$in>;

        close $out                              # Call close(  ) in non-void context so
            or carp "close failed: $OS_ERROR";  #     close(  ) returns false on failure

        close $in;                              # Call close(  ) in void context so
                                                #     close(  ) throws exception on failure
    }

While this may seem like an improvement (more flexible, more Perlish), it's actually a step backwards in terms of code reliability. The problem is that it's far too easy to call a subroutine or function in a non-void context and still not actually test it. For example:

    
    # Change unacceptable failure behaviour to throw exceptions instead...
    use Fatal qw( :void locate_and_open );

    # and later...

    for my $filename  (@source_files) {
        my $fh = locate_and_open($filename);
        my $head = load_header_from($fh);
        print $head;
    }

Here, locate_and_open( ) is upgraded to throw exceptions on void-context failure. Unfortunately, it isn't called in a void context. It's called in scalar context, so it still returns its usual undef-on-failure. But, once again, the return value isn't subsequently checked.

Non-void context doesn't always imply a test, so a use Fatal qw( :void funcname ) may make your code appear to be more robust, without actually making that code more robust...which makes that code less robust.

    Previous Page
    Next Page