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.
 |