SYNOPSIS
========

        use Nice::Try;

        print( "Hello, I want to try\n" );
        # Try out {
        print( "this piece of code\n" );
        try 
        {
            # Not so sure }
            print( "I am trying!\n" );
            die( "Bye cruel world..." );
            # Never going to reach this
            return( 1 );
        }
        # Some comment
        catch( Exception $e ) {
            return( "Caught an exception $e" );
        }
        # More comment with space too

        catch( $e ) {
            print( "Got an error: $e\n" );
        }
        finally
        {
            print( "Cleaning up\n" );
        }
        print( "Ok, then\n" );

When run, this would produce, as one would expect:

        Hello, I want to try
        this piece of code
        I am trying!
        Got an error: Bye cruel world... at ./some/script.pl line 18.
        Cleaning up
        Ok, then

Also since version 1.0.0,
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} is context
aware:

        use Want; # an awesome module which extends wantarray
        sub info
        {
            my $self = shift( @_ );
            try
            {
                # Do something
                if( want('OBJECT') )
                {
                    return( $self );
                }
                elsif( want('CODE') )
                {
                    # dummy code ref for example
                    return( sub{ return( $name ); } );
                }
                elsif( want('LIST') )
                {
                    return( @some_data );
                }
                elsif( want('ARRAY') )
                {
                    return( \@some_data );
                }
                elsif( want('HASH') )
                {
                    return({ name => $name, location => $city });
                }
                elsif( want('REFSCALAR') )
                {
                    return( \$name );
                }
                elsif( want('SCALAR' ) )
                {
                    return( $name ); # regular string
                }
                elsif( want('VOID') )
                {
                    return;
                }
            }
            catch( $e )
            {
                $Logger->( "Caught exception: $e" );
            }
        }

        # regular string context
        my $name = $o->info;
        # code context
        my $name = $o->info->();
        # list context like wantarray
        my @data = $o->info;
        # hash context
        my $name = $o->info->{name};
        # array context
        my $name = $o->info->[2];
        # object context
        my $name = $o->info->another_method;
        # scalar reference context
        my $name = ${$o->info};

VERSION
=======

        v1.0.0

DESCRIPTION
===========

[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} is a
lightweight implementation of Try-Catch exception trapping block using
[perl filter](https://metacpan.org/pod/perlfilter){.perl-module}. It
behaves like you would expect.

Here is a list of its distinctive features:

-   No routine to import like `Nice::Try qw( try catch )`. Just add
    `use Nice::Try` in your script
-   Properly report the right line number for the original error message
-   Allows embedded try-catch block within try-catch block, such as:

            use Nice::Try;

            print( "Wow, something went awry: ", &gotcha, "\n" );

            sub gotcha
            {
                print( "Hello, I want to try\n" );
                # Try out {
                CORE::say( 'this piece' );
                try 
                {
                    # Not so sure }
                    print( "I am trying!\n" );
                    try
                    {
                        die( "Bye cruel world..." );
                        return( 1 );
                    }
                    catch( $err )
                    {
                        die( "Dying again with embedded error: '$err'" );
                    }
                }
                catch( Exception $e ) {
                    return( "Caught an exception \$e" );
                }
                catch( $e ) {
                    try
                    {
                        print( "Got an error: $e\n" );
                        print( "Trying something else.\n" );
                        die( "No really, dying out... with error: $e\n" );
                    }
                    catch( $err2 )
                    {
                        return( "Returning from catch L2 with error '$err2'" );
                    }
                }
                CORE::say( "Ok, then" );
            }

-   No need for semicolon on the last closing brace
-   It does not rely on perl regular expression, but instead uses
    [PPI](https://metacpan.org/pod/PPI){.perl-module} (short for \"Perl
    Parsing Interface\").
-   Variable assignment in the catch block works. For example:

            try
            {
                # Something or
                die( "Oops\n" );
            }
            catch( $funky_variable_name )
            {
                return( "Oh no: $funky_variable_name" );
            }

-   `$@` is always available too
-   You can return a value from try-catch blocks, even with embedded
    try-catch blocks
-   It recognises `@_` inside try-catch blocks, so you can do something
    like:

            print( &gotme( 'Jacques' ), "\n" );

            sub gotme
            {
                try
                {
                    print( "I am trying my best $_[0]!\n" );
                    die( "But I failed\n" );
                }
                catch( $some_reason )
                {
                    return( "Failed: $some_reason" );
                }
            }

    Would produce:

            I am trying my best Jacques!
            Failed: But I failed

-   `try` or `catch` blocks can contain flow control keywords such as
    `next`, `last` and `redo`

            while( defined( my $product = $items->[++$i] ) )
            {
                try
                {
                    # Do something
                    last if( !$product->active );
                }
                catch( $oops )
                {
                    $log->( "Error: $oops" );
                    last;
                }
            }
            continue
            {
                try
                {
                    if( $product->region eq 'Asia' )
                    {
                        push( @asia, $product );
                    }
                    else
                    {
                        next;
                    }
                }
                catch( $e )
                {
                    $log->( "An unexpected error has occurred. Is $product an object? $e" );
                    last;
                }
            }

-   Can be used with or without a `catch` block
-   Supports a `finally` block called in void context for cleanup for
    example
-   [Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} is
    rich context aware, which means it can provide you with a super
    granual context on how to return data back to the caller based on
    the caller\'s expectation, by using a module like
    [Want](https://metacpan.org/pod/Want){.perl-module}.
-   Call to [\"caller\" in
    perlfunc](https://metacpan.org/pod/perlfunc#caller){.perl-module}
    will return the correct entry in call stack

            #!/usr/bin/perl
            BEGIN
            {
                use strict;
                use warnings;
                use Nice::Try;
            };

            {
                &callme();
            }

            sub callme
            {
                try
                {
                    my @info = caller(1); # or my @info = caller;
                    print( "Called from package $info[0] in file $info[1] at line $info[2]\n" );
                }
                catch( $e )
                {
                    print( "Got an error: $e\n" );
                }
            }

    WIll yield: `Called from package main in file ./test.pl at line 10`

WHY USE IT?
===========

There are quite a few implementations of try-catch blocks in perl, and
they can be grouped in 4 categories:

1 Try-Catch as subroutines

:   For example
    [Try::Tiny](https://metacpan.org/pod/Try::Tiny){.perl-module}

2 Using Perl Filter

:   For example
    [Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module},
    [Try::Harder](https://metacpan.org/pod/Try::Harder){.perl-module}

3 Using [Devel::Declare](https://metacpan.org/pod/Devel::Declare){.perl-module}

:   For example
    [TryCatch](https://metacpan.org/pod/TryCatch){.perl-module}

4 Others

:   For example
    [Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module}
    and now perl with [version 5.33.7 using experimental
    feature](https://perldoc.perl.org/blead/perlsyn#Try-Catch-Exception-Handling){.perl-module}.

Group 1 requires the use of semi-colons like:

        try
        {
            # Something
        }
        catch
        {
            # More code
        };

It also imports the subroutines `try` and `catch` in your namespace.

And you cannot do exception variable assignment like `catch( $err )`

In group 2,
[Try::Harder](https://metacpan.org/pod/Try::Harder){.perl-module} does a
very nice work, but relies on perl regular expression with
[Text::Balanced](https://metacpan.org/pod/Text::Balanced){.perl-module}
and that makes it susceptible to failure if the try-catch block is not
written as it expects it to be. For example if you put comments between
try and catch, it would not work anymore. This is because parsing perl
is famously difficult. Also, it does not do exception variable
assignment, or catch filtered based on exception class like:

        try
        {
            # Something
            die( Exception->new( "Failed!" ) );
        }
        catch( Exception $e )
        {
            # Do something if exception is an Exception class
        }

See [\"die\" in
perlfunc](https://metacpan.org/pod/perlfunc#die){.perl-module} for more
information on dying with an object.

Also [Try::Harder](https://metacpan.org/pod/Try::Harder){.perl-module}
will die if you use only `try` with no catch, such as:

        use Try::Harder;
        try
        {
            die( "Oops\n" );
        }
        # Will never reach this
        print( "Got here with $@\n" );

In this example, the print line will never get executed. With
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} you can
use `try` alone as an equivalent of [\"eval\" in
perlfunc](https://metacpan.org/pod/perlfunc#eval){.perl-module} and the
`$@` will be available too. So:

        use Nice::Try;
        try
        {
            die( "Oops\n" );
        }
        print( "Got here with $@\n" );

will produces:

        Got here with Oops

In group 3, [TryCatch](https://metacpan.org/pod/TryCatch){.perl-module}
was working wonderfully, but was relying on
[Devel::Declare](https://metacpan.org/pod/Devel::Declare){.perl-module}
which was doing some esoteric stuff and eventually the version 0.006020
broke [TryCatch](https://metacpan.org/pod/TryCatch){.perl-module} and
there seems to be no intention of correcting this breaking change.

In group 4, there is
[Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module},
which is a great alternative if you do not care about exception class
filter (it supports variable assignment since 2020-08-01 with version
0.18).

Although, the following script would not work under
[Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module}
:

        BEGIN
        {
            use strict;
            use warnings;
            use Syntax::Keyword::Try;
        };

        {
            &callme();
        }

        sub callme
        {
            try {
                print( "Hello there\n" );
            }
            catch ($e) {
                print( "Got an error: $e\n" );
            }
        }

This will trigger the following error:

        syntax error at ./test.pl line 18, near ") {"
        syntax error at ./test.pl line 21, near "}"
        Execution of ./test.pl aborted due to compilation errors.

That is because
[Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module}
expects to be `used` outside of a BEGIN block like this:

        use strict;
        use warnings;
        use Syntax::Keyword::Try;

        # Rest of the script, same as above

Of course, with
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module}, there is
no such constraint. You can [\"use\" in
perlfunc](https://metacpan.org/pod/perlfunc#use){.perl-module}
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} inside or
outside of a `BEGIN` block indistinctively.

Since [perl version
5.33.7](https://perldoc.perl.org/blead/perlsyn#Try-Catch-Exception-Handling){.perl-module}
you can use the try-catch block using an experimental feature which may
be removed in future versions, by writing:

        use feature 'try'; # will emit a warning this is experimental

This new feature supports try-catch block and variable assignment, but
no exception class, nor support for `finally` block, so you can do:

        try
        {
            # Oh no!
            die( "Argh...\n" );
        }
        catch( $oh_well )
        {
            return( $self->error( "Something went awry: $oh_well" ) );
        }

But **you cannot do**:

        try
        {
            # Oh no!
            die( MyException->new( "Argh..." ) );
        }
        catch( MyException $oh_well )
        {
            return( $self->error( "Something went awry with MyException: $oh_well" ) );
        }
        # No support for 'finally' yet in perl version 5.33.7
        finally
        {
            # do some cleanup here
        }

It is probably a matter of time until this is fully implemented in perl
as a regular non-experimental feature.

So, [Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} is
quite unique and fill the missing features, but because it is purely in
perl and not an XS module, it is slower than XS module like
[Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module}.
I am not sure the difference would be that noticeable, since the parsing
by [PPI](https://metacpan.org/pod/PPI){.perl-module} is now done using
an XS module, which makes things very fast.

FINALLY
=======

Like with other language such as Java or JavaScript, the `finally` block
will be executed even if the `try` or `catch` block contains a return
statement.

This is useful to do some clean-up. For example:

        try
        {
            # Something worth dying
        }
        catch( $e )
        {
            return( "I failed: $e" );
        }
        finally
        {
            # Do some mop up
            # This would be reached even if catch already returned
            # Putting return statement here does not actually return anything.
            # This is only for clean-up
        }

However, because this is designed for clean-up, it is called in void
context, so any `return` statement there will not actually return
anything back to the caller.

CATCHING OR NOT CATCHING?
=========================

[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} can be
used with a single `try` block which will, in effect, behaves like an
eval and the special variable `$@` will be available as always.

        try
        {
            die( "Oh no, something went wrong!\n" );
        }
        print( "Got here with $@\n" );

or even:

        try
        {
            die( "Oh no, something went wrong!\n" );
        }
        catch( $e ); # Not very meaningful, but it will work
        print( "Got here with $@\n" );

However, if you decide to catch class exceptions, make sure to add a
default `catch( $e )`. For example:

        try
        {
            die( MyException->new( "Oh no" ) );
        }
        print( "Got here with $@\n" );

will work and `print` will display \"Got here with Oh no\". However:

        try
        {
            die( MyException->new( "Oh no" ) );
        }
        catch( Some::Exception $e )
        {
            # won't reach here
        }

will make your process die because of the exception not being caught,
thus you might want to do instead:

        try
        {
            die( MyException->new( "Oh no" ) );
        }
        catch( Some::Exception $e )
        {
            # won't reach here
        }
        catch( $default )
        {
            print( "Got you! Error was: $default\n" );
        }

And the last catch will catch the exception.

Since, try-catch block can be nested, the following would work too:

        try
        {
            try
            {
                die( MyException->new( "Oh no" ) );
            }
            catch( Some::Exception $e )
            {
                # won't reach here
            }
        }
        catch( MyException $e )
        {
            print( "Got you! MyException was: $e\n" );
        }
        # to play it safe
        catch( $e )
        {
            # do something about it
        }

LOOPS
=====

Since version v0.2.0
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} supports
the use of flow control keywords such as `next`, `last` and `redo`
inside try-catch blocks. For example:

        my @names = qw( John Jack Peter Paul Mark );
        for( $i..$#names )
        {
            try
            {
                next if( $i == 2 );
                # some more code...
            }
            catch( $e )
            {
                print( "Got exception: $e\n" );
            }
        }

It also works inside the catch block or inside the `continue` block:

        while( defined( my $product = $items->[++$i] ) )
        {
            # Do something
        }
        continue
        {
            try
            {
                if( $product->region eq 'Asia' )
                {
                    push( @asia, $product );
                }
                else
                {
                    next;
                }
            }
            catch( $e )
            {
                $log->( "An unexpected error has occurred. Is $product an object? $e" );
                last;
            }
        }

Control flow with labels also work

        ELEM: foreach my $n ( @names )
        {
            try
            {
                $n->moveAfter( $this );
                next ELEM if( $n->value == 1234567 );
            }
            catch( $oops )
            {
                last ELEM;
            }
        }

However, if you enclose a try-catch block inside another block, use of
`next`, `last` or `redo` will silently not work. This is due to perl
control flow. See
[perlsyn](https://metacpan.org/pod/perlsyn){.perl-module} for more
information on this. For example, the following would not yield the
desired outcome:

        ELEM: foreach my $n ( @names )
        {
            { # <--- Here is the culprit
                try
                {
                    $n->moveAfter( $this );
                    # This next statement will not do anything.
                    next ELEM if( $n->value == 1234567 );
                }
                catch( $oops )
                {
                    # Neither would this one.
                    last ELEM;
                }
            }
        }

CONTEXT AWARENESS
=================

[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} provides a
high level of granularity about the context in which your subroutine was
called.

Normally, you would write something like this, and it works as always:

        sub info
        {
            try
            {
                # do something here
                if( wantarray() )
                {
                    return( @list_of_values );
                }
                # caller just want a scalar
                elsif( defined( wantarray() ) )
                {
                    return( $name );
                }
                # otherwise if undefined, it means we are called in void context, like:
                # $o->info; with no expectation of return value
            }
            catch( $e )
            {
                print( "Caught an error: $e\n" );
            }
        }

THe above is nice, but how do you differentiate cases were your caller
wants a simple returned value and the one where the caller wants an
object for chaining purpose, or if the caller wants an hash or array
reference in return?

For example:

        my $val = $o->info->[2]; # wants an array reference
        my $val = $o->info->{name} # wants an hash reference
        # etc...

Now, you can do the following:

        use Want; # an awesome module which extends wantarray
        sub info
        {
            my $self = shift( @_ );
            try
            {
                # Do something
                # 
                # same as wantarray() == 1
                if( want('LIST') )
                {
                    return( @some_data );
                }
                # same as: if( defined( wantarray() ) && !wantarray() )
                elsif( want('SCALAR' ) )
                {
                    return( $name ); # regular string
                }
                # same as !defined( wantarray() )
                elsif( want('VOID') )
                {
                    return;
                }
                # For the other context below, wantarray is of no help
                if( want('OBJECT') )
                {
                    return( $obj ); # useful for chaining
                }
                elsif( want('CODE') )
                {
                    # dummy code ref for example
                    return( sub{ return( $name ); } );
                }
                elsif( want('ARRAY') )
                {
                    return( \@some_data );
                }
                elsif( want('HASH') )
                {
                    return({ name => $name, location => $city });
                }
            }
            catch( $e )
            {
                $Logger->( "Caught exception: $e" );
            }
        }

Thus this is particularly useful if, for example, you want to
differentiate if the caller just wants a return string, or an object for
chaining.

[\"wantarray\" in
perlfunc](https://metacpan.org/pod/perlfunc#wantarray){.perl-module}
would not know the difference, and other try-catch implementation would
not let you benefit from using
[Want](https://metacpan.org/pod/Want){.perl-module}.

For example:

        my $val = $o->info; # simple regular scalar context; but...
        # here, we are called in object context and wantarray is of no help to tell the difference
        my $val = $o->info->another_method;

Other cases are:

        # regular string context
        my $name = $o->info;
        # list context like wantarray
        my @data = $o->info;

        # code context
        my $name = $o->info->();
        # hash context
        my $name = $o->info->{name};
        # array context
        my $name = $o->info->[2];
        # object context
        my $name = $o->info->another_method;

See [Want](https://metacpan.org/pod/Want){.perl-module} for more
information on how you can benefit from it.

Currently lvalues are no implemented and will be in future releases.

Also, for this rich context awareness to be used, obviously try-catch
would need to be inside a subroutine, otherwise there is no rich context
other than the one the regular [\"wantarray\" in
perlfunc](https://metacpan.org/pod/perlfunc#wantarray){.perl-module}
provides.

DEBUGGING
=========

And to have
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} save the
filtered code to a file, pass it the `debug_file` parameter like this:

        use Nice::Try debug_file => './updated_script.pl';

You can also call your script using
[Filter::ExtractSource](https://metacpan.org/pod/Filter::ExtractSource){.perl-module}
like this:

        perl -MFilter::ExtractSource script.pl > updated_script.pl

or add `use Filter::ExtractSource` inside it.

In the updated script produced, you can add the line calling
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} to:

        use Nice::Try no_filter => 1;

to avoid [Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module}
from filtering your script

If you want
[Nice::Try](https://metacpan.org/pod/Nice::Try){.perl-module} to produce
human readable code, pass it the `debug_code` parameter like this:

        use Nice::Try debug_code => 1;

CREDITS
=======

Credits to Stephen R. Scaffidi for his implementation of
[Try::Harder](https://metacpan.org/pod/Try::Harder){.perl-module} from
which I borrowed some code.

AUTHOR
======

Jacques Deguest \<`jack@deguest.jp`{classes="ARRAY(0x56381478d098)"}\>

SEE ALSO
========

[PPI](https://metacpan.org/pod/PPI){.perl-module},
[Filter::Util::Call](https://metacpan.org/pod/Filter::Util::Call){.perl-module},
[Try::Harder](https://metacpan.org/pod/Try::Harder){.perl-module},
[Syntax::Keyword::Try](https://metacpan.org/pod/Syntax::Keyword::Try){.perl-module},
[Exception::Class](https://metacpan.org/pod/Exception::Class){.perl-module}

COPYRIGHT & LICENSE
===================

Copyright (c) 2020-2021 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated
files under the same terms as Perl itself.
