NAME

    Syntax::Keyword::Try - a try/catch/finally syntax for perl

SYNOPSIS

     use Syntax::Keyword::Try;
    
     sub foo
     {
        try {
           attempt_a_thing();
           return "success";
        }
        catch {
           warn "It failed - $@";
           return "failure";
        }
     }

DESCRIPTION

    This module provides a syntax plugin that implements exception-handling
    semantics in a form familiar to users of other languages, being built
    on a block labeled with the try keyword, followed by at least one of a
    catch or finally block.

    As well as providing a handy syntax for this useful behaviour, this
    module also serves to contain a number of code examples for how to
    implement parser plugins and manipulate optrees to provide new syntax
    and behaviours for perl code.

KEYWORDS

 try

       try {
          STATEMENTS...
       }
       ...

    A try statement provides the main body of code that will be invoked,
    and must be followed by either a catch statement, a finally statement,
    or both.

    Execution of the try statement itself begins from the block given to
    the statement and continues until either it throws an exception, or
    completes successfully by reaching the end of the block. What will
    happen next depends on the presence of a catch or finally statement
    immediately following it.

    The body of a try {} block may contain a return expression. If
    executed, such an expression will cause the entire containing function
    to return with the value provided. This is different from a plain eval
    {} block, in which circumstance only the eval itself would return, not
    the entire function.

    The body of a try {} block may contain loop control expressions (redo,
    next, last) which will have their usual effect on any loops that the
    try {} block is contained by.

    The parsing rules for the set of statements (the try block and its
    associated catch and finally) are such that they are parsed as a self-
    contained statement. Because of this, there is no need to end with a
    terminating semicolon.

    Note (especially to users of Try::Tiny and similar) that the try {}
    block itself does not necessarily stop exceptions thrown inside it from
    propagating outside. It is the presence of a later catch {} block which
    causes this to happen. A try with only a finally and no catch will
    still propagate exceptions up to callers as normal.

 catch

       ...
       catch {
          STATEMENTS...
       }

    Or

       ...
       catch my $var {
          STATEMENTS...
       }

    Experimental; since version 0.12.

    A catch statement provides a block of code to the preceding try
    statement that will be invoked in the case that the main block of code
    throws an exception. The catch block can inspect the raised exception
    by looking in $@ in the usual way. Optionally, a new lexical variable
    can be introduced to store the exception in. This new form is
    experimental and is likely to change in a future version, as part of
    the wider attempt to introduce typed dispatch. Using it will provoke an
    experimental category warning on supporting perl versions.

    Presence of this catch statement causes any exception thrown by the
    preceding try block to be non-fatal to the surrounding code. If the
    catch block wishes to optionally handle some exceptions but not others,
    it can re-raise it (or another exception) by calling die in the usual
    manner.

    As with try, the body of a catch {} block may also contain a return
    expression, which as before, has its usual meaning, causing the entire
    containing function to return with the given value. The body may also
    contain loop control expressions (redo, next or last) which also have
    their usual effect.

    If a catch statement is not given, then any exceptions raised by the
    try block are raised to the caller in the usual way.

 finally

       ...
       finally {
          STATEMENTS...
       }

    A finally statement provides a block of code to the preceding try
    statement (or try/catch pair) which is executed afterwards, both in the
    case of a normal execution or a thrown exception. This code block may
    be used to provide whatever clean-up operations might be required by
    preceding code.

    Because it is executed during a stack cleanup operation, a finally {}
    block may not cause the containing function to return, or to alter the
    return value of it. It also cannot see the containing function's @_
    arguments array (though as it is block scoped within the function, it
    will continue to share any normal lexical variables declared up until
    that point). It is protected from disturbing the value of $@. If the
    finally {} block code throws an exception, this will be printed as a
    warning and discarded, leaving $@ containing the original exception, if
    one existed.

VALUE SEMANTICS

      Warning: the feature described in this section is experimental. This
      experiment may be stablised in a later version, or may be altered or
      removed without further notice. It is present here for testing and
      evaluation purposes.

      Additionally, on perl versions 5.18 and later, it will produce a
      warning in the experimental category.

    The syntax provided by this module may be used as a value-yielding
    expression. Because this syntax is new, experimental, and somewhat
    surprising, it must be specifically requested by name try_value:

       use Syntax::Keyword::Try qw( try try_value );
    
       my $result = try do { ... } catch { ... };

    Also, on Perl versions 5.24 and later:

       my $result = try do { ... } finally { ... };
    
       my $result = try do { ... } catch { ... } finally { ... };

    Specifically, note that the expression must be spelled as try do { ...
    } so that the syntax is distinct from that used by control-flow
    statements. The interposed do keyword reminds the reader, and instructs
    the syntax parser, that this will be an expression, not a statement. It
    is not necessary to similarly notate the catch or finally blocks.

    In this case, the syntax behaves syntactically like an expression, and
    may appear anywhere a normal expression is allowed. It follows similar
    semantics to the purely control-flow case; if the code in the try block
    does not throw an exception, then the expression as a whole yields
    whatever value the try expression did. If it fails, then the catch
    block is executed and the expression yields its resulting value
    instead. A finally block, if present, will be evaluated for
    side-effects before the rest of the expression returns.

    Remember that, as in the control-flow case, the return keyword will
    cause the entire containing function to return, not just the try block.

OTHER MODULES

    There are already quite a number of modules on CPAN that provide a
    try/catch-like syntax for Perl.

      * Try

      * TryCatch

      * Try::Tiny

      * Syntax::Feature::Try

    They are compared here, by feature:

 True syntax plugin

    Like Try and Syntax::Feature::Try, this module is implemented as a true
    syntax plugin, allowing it to provide new parsing rules not available
    to simple functions. Most notably here it means that the resulting
    combination does not need to end in a semicolon.

    In comparison, Try::Tiny is plain perl and provides its functionality
    using regular perl functions; as such its syntax requires the trailing
    semicolon.

    TryCatch is a hybrid that uses Devel::Declare to parse the syntax tree.

 @_ in a try or catch block

    Because the try and catch block code is contained in a true block
    rather than an entire anonymous subroutine, invoking it does not
    interfere with the @_ arguments array. Code inside these blocks can
    interact with the containing function's array as before.

    This feature is unique among these modules; none of the others listed
    have this ability.

 return in a try or catch block

    Like TryCatch and Syntax::Feature::Try, the return statement has its
    usual effect within a subroutine containing syntax provided by this
    module. Namely, it causes the containing sub itself to return.

    In comparison, using Try or Try::Tiny mean that a return statement will
    only exit from the try block.

 next/last/redo in a try or catch block

    The loop control keywords of next, last and redo have their usual
    effect on dynamically contained loops.

    Syntax::Feature::Try documents that these do not work there. The other
    modules make no statement either way.

 Value Semantics

    Like Try and Syntax::Feature::Try, the syntax provided by this module
    only works as a syntax-level statement and not an expression when the
    experimental try_value feature described above has not been enabled.
    You cannot assign from the result of a try block. Additionally,
    final-expression value semantics do not work, so it cannot be contained
    by a do block to yield this value.

    In comparison, the behaviour implemented by Try::Tiny can be used as a
    valued expression, such as assigned to a variable or returned to the
    caller of its containing function. Such ability is provided by this
    module if the experimental try_value feature is enabled, though it must
    be spelled differently as try do { ... }.

 try without catch

    Like Syntax::Feature::Try, the syntax provided by this module allows a
    try block to be followed by only a finally block, with no catch. In
    this case, exceptions thrown by code contained by the try are not
    suppressed, instead they propagate as normal to callers. This matches
    the behaviour familiar to Java or C++ programmers.

    In comparison, the code provided by Try and Try::Tiny always suppress
    exception propagation even without an actual catch block.

    The TryCatch module does not allow a try block not followed by catch.

 Typed catch

    Like Try and Try::Tiny, this module makes no attempt to perform any
    kind of typed dispatch to distinguish kinds of exception caught by
    catch blocks.

    TryCatch and Syntax::Feature::Try both attempt to provide a kind of
    typed dispatch where different classes of exception are caught by
    different blocks of code, or propagated up entirely to callers.

    This is likely to be the next experimental development on this module,
    in ongoing preparation for a time when it can be moved into core perl
    syntax. While at first I was heistant to implement this as a
    special-case in try/catch syntax, my other work thinking about the
    codenamed "dumbmatch" syntax feature leads me to thinking that actually
    typed dispatch of catch blocks is sufficiently different from value
    dispatch in a more general case (such as "dumbmatch"). Exception
    dispatch in perl needs to handle both isa and string regexp testing at
    the same site.

    My latest thinking on this front may involve some syntax such as:

       try {
          ...
       }
       catch my $e
          (isa Some::Exception::Class) { ... },
          (=~ m/^An error message /)   { ... }

    or

       try {
          ...
       }
       catch ($e isa Some::Exception::Class) { ... },
             ($e =~ m/^An error message /)   { ... }

    Or maybe the catch keyword would be repeated per line, though that then
    involves repeating the error variable name also:

       try {
          ...
       }
       catch my $e (isa Some::Exception::Class) { ... }
       catch my $e (=~ m/^An error message /)   { ... }

    or

       try {
          ...
       }
       catch ($e isa Some::Exception::Class) { ... }
       catch ($e =~ m/^An error message /)   { ... }

    The design thoughts continue on the RT ticket
    https://rt.cpan.org/Ticket/Display.html?id=123918.

WITH OTHER MODULES

 Future::AsyncAwait

    As of Future::AsyncAwait version 0.10 and Syntax::Keyword::Try version
    0.07, cross-module integration tests assert that basic try/catch blocks
    inside an async sub work correctly, including those that attempt to
    return from inside try.

       use Future::AsyncAwait;
       use Syntax::Keyword::Try;
    
       async sub attempt
       {
          try {
             await func();
             return "success";
          }
          catch {
             return "failed";
          }
       }

KNOWN BUGS

 Thread-safety at load time cannot be assured before perl 5.16

    On perl versions 5.16 and above this module is thread-safe.

    On perl version 5.14 this module is thread-safe provided that it is
    used before any additional threads are created.

    However, when using 5.14 there is a race condition if this module is
    loaded late in the program startup, after additional threads have been
    created. This leads to the potential for it to be started up multiple
    times concurrently, which creates data races when modifying internal
    structures and likely leads to a segmentation fault, either during load
    or soon after when more code is compiled.

    As a workaround, for any such program that creates multiple threads,
    loads additional code (such as dynamically-discovered plugins), and has
    to run on 5.14, it should make sure to

       use Syntax::Keyword::Try;

    early on in startup, before it spins out any additional threads.

    (See also https://rt.cpan.org/Public/Bug/Display.html?id=123547)

 $@ is not local'ised by try do before perl 5.24

    On perl versions 5.24 and above, or when using only control-flow
    statement syntax, $@ is always correctly localised.

    However, when using the experimental value-yielding expression version
    try do {...} on perl versions 5.22 or older, the localisation of $@
    does not correctly apply around the expression. After such an
    expression, the value of $@ will leak out if a failure happened and the
    catch block was invoked, overwriting any previous value that was
    visible there.

    (See also https://rt.cpan.org/Public/Bug/Display.html?id=124366)

ACKNOWLEDGEMENTS

    With thanks to Zefram, ilmari and others from irc.perl.org/#p5p for
    assisting with trickier bits of XS logic.

AUTHOR

    Paul Evans <leonerd@leonerd.org.uk>

