http://www.perlmonks.org?node_id=673060

Most esteemed monks,

I've long been a fan of Perl's Fatal module, which allows one to replace both user-defined and built-in functions with equivalents that throw an exception on failure. However due to the limitations of Perl before 5.10, Fatal had a package-wide scope, and this would keep me awake at night. What if someone introduced Fatal to their legacy code and introduced/revealed new bugs, due to Fatal's wide-reaching consequences?

However with the powers of Perl 5.10 and the %^H lexical hints hash, we have the power to create Fatal-style changes with only lexical scope. I've put together a proof-of-concept pragma to do this, which currently works as follows:

use exceptions qw(open close); open(my $fh, '<', 'some_file'); # Throws exception on failure { no exceptions qw(open); open(my $other_fh, '<', 'some_other_file'); # fails silent close($fh); # This still throws an exception. no exceptions; # Turns off exceptions entirely. close($other_fh); # Fails silent. } close($yet_another_fh); # Throws exception on failure again.

The code as it stands has a few adjustments that I need to make, and obviously a lot of tests to ensure that code this cool works correctly. I'm primarily looking for interface and naming suggestions, although I'm happy for this to start a broard discussion.

Some matters I'm still considering:

Those who are curious should also know that this module is currently being discussed on moduless@perl.org. If you really want you can also download my proof of concept, but you should be aware that's it's unfinished, buggy, emits warnings, ugly, is poorly documented and should not be used for production code. Using the code, or even looking at it, may cause the Earth to fall into the sun and your hair to fall out. It also only works under 5.10. It's a proof of concept, not a finished piece of art. I am seeking feedback on the interface, not the code, at this time.

Thanks in advance for all your thoughts,

Update: After many discussions on p5p it looks like we'll be getting a new Fatal.pm module with 5.10.1. The new pragma version will be called autodie.

Replies are listed 'Best First'.
Re: RFC: Lexical Fatal.pm for Perl 5.10
by BrowserUk (Patriarch) on Mar 09, 2008 at 08:25 UTC

    1. With regard to naming:

      Roget's says: "use fatal for something which has caused someone's death; use lethal for something which is capable of killing someone."

      So maybe use lethal; makes more sense.

    2. With regard to the interface. It would be nice if the lexical module took care of installing and backing out the exception handler. It's not onerous work, but is ripe for automation. Something like:
      { use lethal qw[ ... ], sub { if( $^exception eq 'something' ) { ## deal with it ... return; } else { die $^exception; ## re-throw } }; ... ## protected code here }

      Ie. The module would save the current $SIG{__DIE__} handler and install a the users callback and restore the original when leaving scope or when no lethal; is seen.

      I'm not sure all of that is possible, never mind how to do it. But I like the idea of it.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      maybe use lethal; makes more sense.

      I love the suggestion. Thank-you! Project named internally to 'lethal'. ;)

      It would be nice if the lexical module took care of installing and backing out the exception handler.

      While I appreciate the desire here, I'd argue that lethal is the wrong place to do it. I expect most Perl developers are used to seeing exception handling occur at the bottom of code. In traditional Perl 5:

      eval { scrub($decks); weigh($anchor); sail_to($destination); }; if ($@) { warn "Uh oh, we had a problem with $@"; }

      Or when using the Error module:

      use Error; try { scrub($decks); weigh($anchor); sail_to($destination); } catch Error::Anchor with { my $E = shift; warn "The anchor's stuck due to $E"; } otherwise { my $E = shift; warn "We're not sailing due to $E"; };

      In both cases (and in most languages), the exceptions are handled at the end, or in the calling code. I'd certainly be surprised to see the exception handler before the code that may throw the exception.

      However I will be mindful to ensure that lethal inherits cleanly, so this can be implemented easily in a child module if you think it's a good idea. ;)

      Many thanks for the excellent ideas,

        In both cases (and in most languages), the exceptions are handled at the end, or in the calling code.

        Well, if you use the $SIG{__DIE__} = sub (...) approach, this has to be done up front. The two approachs, signal handler and block eval serve slightly different purposes, but can be mixed I think.

        Anyway, good luck with your module :)


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: RFC: Lexical Fatal.pm for Perl 5.10
by hossman (Prior) on Mar 09, 2008 at 07:58 UTC
    What should the module be called?
    ...
    Should this module even exist? Am I duplicating existing work? Note that I'm using Fatal to do all the heavy lifting internally. To the best of my knowledge there's no existing way to use Fatal with lexical scope.

    why not just make this a patch for Fatal triggered by an option on the use line (the option would be implicit for "no" since it's not currently supported and would imply lexicality anyway)?

    use Fatal qw(:lexical open close); open(my $fh, '<', 'some_file'); # Throws exception on failure { no Fatal qw(open); open(my $other_fh, '<', 'some_other_file'); # fails silent close($fh); # This still throws an exception. no Fatal; # Turns off exceptions entirely. close($other_fh); # Fails silent. } close($yet_another_fh); # Throws exception on failure again.
      why not just make this a patch for Fatal triggered by an option on the use line

      This is an excellent idea, and one I really should have considered and mentioned in my original post. I'm very happy with the idea of a patch to Fatal; it keeps everything together, and it certainly simplifies the code.

      Downsides of patching Fatal are:

      • We'd be changing the behaviour of a core module, which means waiting until 5.10.1 before it's deployed, or making Fatal dual-lifed (core + CPAN).
      • I'd need to get sign-off from p5p, Lionel, or both. That shouldn't be a problem for a good idea, but it does have additional administrative overhead.
      • We're altering the interface of a very well-known core module to do something that it's never done before. There may be surprises there.
      • The calling syntax doesn't look like a pragma, but the more I look at it the less I mind. Using :lexical I hope makes it pretty obvious what's going on.

      The advantages are that it goes in the core for everyone to enjoy, my ability to reuse code in Fatal becomes much much easier, and the functionality goes in the place where I imagine most people will expect it to be.

      I'll open discussions with p5p to see their thoughts. Of course, I still very much appreciate feedback on the original proposal as well.

      Many thanks,

        I'm very happy with the idea of a patch to Fatal;

        That would be great, but failing that I think it'd be best to have a name which immediately suggests “like Fatal but works lexically”, such as Fatal::Lexical.

        Otherwise if we have, say, both Fatal and Lethal, there's nothing indicating which name has which behaviour; it seems arbitrary to anybody who doesn't already know.

Re: RFC: Lexical Fatal.pm for Perl 5.10
by diotalevi (Canon) on Mar 09, 2008 at 18:10 UTC

    I object to your "exceptions" pragma. You are not powerful to prevent any exceptions from being generated by a snippet of code. To truely do that, you must eval wrap all possible functions and opcodes in that scope including the builtin ones and eat all runtime errors. A declaration like "no exceptions" is highly categorical and probably isn't the kind of guarantee that you can sanely promise. It certainly goes beyond your intent of merely affecting the "Fatal" module. Try to stay away from overreaching language when designing your API.

    To be able to say "no exceptions" you need to prevent perl from throwing exceptions in addition to exceptions explicitly written in user code. Examples of runtime code that you'd need to handle are method calls on undefined values, blessing into references, sort routines returning non-numbers, etc. It would be possible to disable all possible exceptions but it seems like it's hard. I'd reserve such strong statements until and unless you can actually make those kinds of guarantees.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      I object to your "exceptions" pragma.

      Do you object to the whole idea, or just the name? The idea is nothing new, we're doing exactly the same thing as Fatal, but on a lexical scope. In other words, open() is simply turned into open() or die under the hood. If you think that Fatal is a good idea, but this pragma is a bad one, then I'm very interested in hearing more of your reasons why. My biggest problem with Fatal was always that it had too wide a scope.

      As per BrowserUk's suggestion, I've renamed the pragma to lethal, avoiding the confusion that a statement like no exceptions may cause. no lethal simple disables the effects of the lethal pragma. Things can still throw exceptions, our pragma just won't have been the cause of them.

      I hope this solves your objection to the name; if it doesn't, then I've misunderstood your node, and I'd love you to elaborate.