Nice::Try(3)

πŸ“¦ version 1.3.17, in alpine 3.24

πŸ’– Nice::Try(3) πŸ’–

okay bestie, this is the manual page for Nice::Try β€” yes, the Nice::Try(3) one β€” and it is lowkey iconic. It’s a real try/catch implementation, and it uses a Perl filter. No cap. 🫢


🌸 NAME 🌸

Nice::Try β€” A real Try Catch Block Implementation Using Perl Filter

Not me being obsessed with a try/catch block that actually slays. The whole page is Nice::Try(3), iykyk. πŸ’…


✨ SYNOPSIS ✨

First vibe: just use Nice::Try; and let the filter do its main character thing.

        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 you run it, it produces exactly what you’d expect, fr fr:

        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 is extended context aware. Look at this beautiful little Want moment:

        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};

And bestie, you also have granular power in the catch block to filter which exception to handle. See more on this in "EXCEPTION CLASS". It’s giving choose-your-own-error-adventure:

        try
        {
            die( Exception->new( "Arghhh" => 401 ) );
        }
        # can also write this as:
        # catch( Exception $oopsie where { $_->message =~ /Arghhh/ && $_->code == 500 } )
        catch( $oopsie isa Exception where { $_->message =~ /Arghhh/ && $_->code == 500 } )
        {
            # Do something to deal with some server error
        }
        catch( $oopsie isa Exception where { $_->message =~ /Arghhh/ && $_->code == 401 } )
        {
            # should reach here.
        }
        catch( $oh_well isa("Exception") ) # or you can also write catch( Exception $oh_well )
        {
            # Default using another way to filter by Exception
        }
        catch( $oopsie where { /Oh no/ } )
        {
            # Do something based on the value of a simple error; not an exception class
        }
        # Default
        catch( $default )
        {
            print( "Unknown error: $default\n" );
        }

πŸ¦‹ VERSION πŸ¦‹

        v1.3.17

πŸ’… DESCRIPTION πŸ’…

Nice::Try is a lightweight implementation of Try-Catch exception trapping block using perl filter. It behaves like you would expect. It’s not doing too much, but what it does, it ate. πŸ”₯

Here is a list of its distinctive features, and yes, they are all kind of iconic:

  • No routine to import like Nice::Try qw( try catch ). Just add use Nice::Try in your script. That’s it. No extra import drama.
  • Properly report the right line number for the original error message. The line number stays correct, which is honestly a slay.
  • 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. Periodt.
  • It does not rely on perl regular expression, but instead uses PPI (short for "Perl Parsing Interface"). That’s the flex.
  • 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" );
        }
  • catch can filter by exception class. For example:
        try
        {
            die( My::Exception->new( "Not alllowed here.", { code => 401 }) );
        }
        catch( My::Exception $e where { $_->code == 500 })
        {
            print( "Oopsie\n" );
        }
        catch( My::Exception $e where { $_->code == 401 })
        {
            print( "Get away!\n" );
        }
        catch( My::Exception $e )
        {
            print( "Got an exception: $e\n" );
        }
        catch( $default )
        {
            print( "Something weird has happened: $default\n" );
        }
        finally
        {
            $dbh->disconnect;
        }

See more on this in "EXCEPTION CLASS".

  • $@ is always available too. Bestie, it’s always there.
  • You can return a value from try-catch blocks, even with embedded try-catch blocks. Return values stay valid, no cap.
  • 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. It’s giving loop-aware main character energy:
        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. Flexible queen behavior.
  • Supports a finally block called in void context for cleanup for example. The finally block will always be called, if present. Always. Even when things get dramatic:
        #!/usr/local/bin/perl
        use v5.36;
        use strict;
        use warnings;
        use Nice::Try;

        try
        {
            die( "Oops" );
        }
        catch( $e )
        {
            say "Caught an error: $e";
            die( "Oops again" );
        }
        finally
        {
            # Some code here that will be executed after the catch block dies
            say "Got here in finally with \$\@ -> $@";
        }

The above would yield something like:

        Caught error: Oops at ./test.pl line 9.
        Oops again at ./test.pl line 14.
        Got here in finally with $@ -> Oops again at ./test.pl line 14.
  • Nice::Try is rich context aware, which means it can provide you with a super granular context on how to return data back to the caller based on the caller's expectation, by using a module like Want. It’s literally reading the room.
  • Call to caller in perlfunc will return the correct entry in call stack. That’s the vibe check passing:
        #!/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? πŸ’…

okay so bestie, there are a lot of try-catch implementations floating around in perl land, and honestly they all sort themselves into 4 little categories:

  1. Try-Catch as subroutines β€” like Try::Tiny, for example
  2. Using Perl Filter β€” like Nice::Try, and Try::Harder
  3. Using Devel::Declare β€” like TryCatch
  4. Others β€” like Syntax::Keyword::Try, and now perl itself starting at version 5.34.0 with an experimental feature <https://perldoc.perl.org/5.34.0/perldelta#Experimental-Try/Catch-Syntax>.

group 1: the subroutine girlies πŸŽ€

Group 1 makes you use semi-colons, like this:

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

it also imports the subroutines try and catch straight into your namespace. and β€” tragic β€” you cannot do exception variable assignment like catch( $err ). πŸ’€

group 2: the regex-parsing era 🌸

In group 2, Try::Harder does a very nice job, but it relies on perl regular expressions with Text::Balanced, which lowkey makes it susceptible to failure if the try-catch block isn't written exactly the way it expects. for example, if you put comments between try and catch, it just stops working. that's because parsing perl is famously difficult β€” no cap. also, it doesn't 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 for more info on dying with an object. 🫢

also Try::Harder will die if you use only try with no catch, like:

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

in this example the print line will never ever get executed. with Nice::Try you can use try alone as an equivalent of eval in perlfunc, and the $@ will be available too. so:

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

will produce:

Got here with Oops

slay. ✨

group 3: rip Devel::Declare 😭

In group 3, TryCatch was working wonderfully, but it was relying on Devel::Declare, which was doing some esoteric stuff, and eventually version 0.006020 broke TryCatch and there seems to be no intention of correcting this breaking change. besides, Devel::Declare is now marked as deprecated and its use is officially discouraged. πŸ’€

also TryCatch does not support any finally block.

group 4: the new kids πŸ‘‘

In group 4 there's Syntax::Keyword::Try, which is a great alternative if you don't care about exception class filter (it supports class exception since 2020-07-21 with version 0.15, and variable assignment since 2020-08-01 with version 0.18).

although β€” the following script would not work under Syntax::Keyword::Try:

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's because Syntax::Keyword::Try 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 there's no such constraint. you can "use" in perlfunc Nice::Try inside or outside of a BEGIN block indistinctively. πŸ¦‹


since perl version 5.33.7 <https://perldoc.perl.org/blead/perlsyn#Try-Catch-Exception-Handling> and now in perl v5.34.0 <https://perldoc.perl.org/5.34.0/perldelta#Experimental-Try/Catch-Syntax>, you can use the try-catch block using an experimental feature β€” which may be removed in future versions, jsyk β€” 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 a finally block until version perl 5.36 released on 2022-05-28 <https://perldoc.perl.org/5.36.0/perldelta> of perl. 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" ) );
}
# Support for 'finally' has been implemented in perl 5.36 released on 2022-05-28
finally
{
    # do some cleanup here
}

an update as of 2022-05-28, perl-v5.36 <https://perldoc.perl.org/5.36.0/perldelta#try/catch-can-now-have-a-finally-block-(experimental)> now supports the experimental finally block. πŸŽ‰

also β€” important β€” the use feature 'try' expression must be in the relevant block where you use try-catch. you cannot just put it in your BEGIN block at the beginning of your script. if you have 3 subroutines using try-catch, you need to put use feature 'try' in each of them. see perl documentation on lexical effect <https://perldoc.perl.org/feature#Lexical-effect> for more explanation on this. πŸ™ˆ

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

as of perl v5.40.0, the try-catch block is now only partly experimental <https://perldoc.perl.org/5.40.0/perldelta#try/catch-feature-is-no-longer-experimental>, but you still need to load it with use feature 'try'. however, at least no warning of it being experimental will be emitted. still, no exception filtering by class though <https://perldoc.perl.org/5.40.0/perlsyn#Try-Catch-Exception-Handling>. 😭

see more information about perl's featured implementation of try-catch in perlsyn <https://perldoc.perl.org/perlsyn#Try-Catch-Exception-Handling>.

so yeah β€” Nice::Try is quite unique, fills in the missing features, and since it uses XS modules for a one-time filtering, it's quite fast. main character energy fr fr. πŸ‘‘


🌈 FINALLY 🌈

like with other languages such as Java or JavaScript, the finally block will be executed even if the try or catch block contains a return statement. it's giving "responsible adult doing clean-up". 🧹

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. so don't try to be clever with it bestie, it will not work. πŸ’…

πŸ¦‹ CATCHING OR NOT CATCHING? πŸ¦‹

Nice::Try can be used with a single try block, which will, in effect, behave 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 😭, so you might want to do this 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 blocks 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
}

πŸ’– EXCEPTION CLASS πŸ’–

So earlier we were like "you can totally use class when you're raising exceptions" and also filter them in like a million ways when you catch them. Let's actually break all those down, bestie.

Here's your menu of options (and fr, just swap out "Exception::Class" for whatever exception class you're obsessed with):

  1. catch( Exception::Class $error_variable ) { }
  2. catch( Exception::Class $error_variable where { $condition } ) { }

Okay so $condition here can be literally anything that fits in a legit perl block. Like, for example:

try
{
    die( Exception->new( "Oh no!", { code => 401 } ) );
}
catch( Exception $oopsie where { $_->code >= 400 && $_->code <= 499 })
{
    # some more handling here
}

Inside that condition block, $_ is always made available and it corresponds to the exception object that got thrown β€” literally the same thing as $oopsie in this example. And $@ is also available with the exception object as its value. ✨

  1. catch( $e isa Exception::Class ) { }

This one's just a little variant of the catch( Exception::Class $e ) {} form. Cute, right?

  1. catch( $e isa('Exception::Class') ) { }

A variant of the one above, for when you feel like using single quotes. πŸ’…

  1. catch( $e isa("Exception::Class") ) { }

Another variant of the one above, but for the double-quote girlies. πŸŽ€

  1. catch( $e isa Exception::Class where { $condition } ) { }
  2. catch( $e isa('Exception::Class') where { $condition } ) { }
  3. catch( $e isa("Exception::Class") where { $condition } ) { }
  4. catch( $e where { $condition } ) { }

Okay important note: this one is not a class exception catching β€” but it's so worth mentioning. Like, look:

try
{
    die( "Something bad happened.\n" );
}
catch( $e where { /something bad/i })
{
    # Do something about it
}
catch( $e )
{
    # Default here
}

Slay. Now let's talk loops, because this is where it gets good. πŸŒ€


πŸ” LOOPS πŸ”

Since version v0.2.0, Nice::Try supports flow control keywords like "next", "last" and "redo" inside try-catch blocks. No cap. 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" );
    }
}

And it also works inside the catch block or inside the "continue" block. Look at this icon:

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

ELEM: foreach my $n ( @names )
{
    try
    {
        $n->moveAfter( $this );
        next ELEM if( $n->value == 1234567 );
    }
    catch( $oops )
    {
        last ELEM;
    }
}
πŸ’€ But listen, this next part is important. If you enclose a try-catch block inside another block, then using "next", "last" or "redo" will silently not work. Not an error, not a warning β€” just silently nothing. That's on perl control flow, so check perlsyn for more info on that. Like, the following would NOT give you the outcome you wanted:
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;
        }
    }
}

See that culprit block? Yeah. Don't do that. πŸ™ˆ


🧠 CONTEXT AWARENESS 🧠

Okay so Nice::Try gives you a high level of granularity about the context in which your subroutine got called. It's giving mind-reading, honestly.

Normally you'd 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" );
    }
}

Which is nice and all, but like… how do you differentiate the case where your caller wants a simple returned value vs. the case where the caller wants an object for chaining purposes, or wants a hash or array reference back? πŸ€”

For example:

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

Well bestie, now you can do this:

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 if( !defined( wantarray() ) )
        elsif( want('VOID') )
        {
            return;
        }
        # For the other contexts 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" );
    }
}

So this is especially useful if, say, you want to differentiate whether the caller just wants a return string or wants an object for chaining. πŸ”₯

Like, "wantarray" in perlfunc genuinely would not know the difference, and other try-catch implementations wouldn't let you benefit from using Want at all.

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 for more info on how you can benefit from it. πŸ’œ

⚠️ Heads up: currently lvalues are not implemented and will be in future releases. Also note that Want does not work within tie-handlers β€” it would trigger a segmentation fault. 😭 Nice::Try detects this and automatically disables support for Want if used inside a tie-handler, reverting to the regular "wantarray" in perlfunc context. So at least it's not gonna blow up on you.

Also, for this rich context awareness to actually be used, obviously the try-catch would need to be inside a subroutine β€” otherwise there's no rich context other than the one the regular "wantarray" in perlfunc provides. Makes sense, right?

And this is especially true when running within an Apache modperl handler, which has no caller. If you use Nice::Try in such a handler, it will kill the Apache process, so you need to disable the use of Want by calling:

use Nice::Try dont_want => 1;

When there's an update to correct this bug from Want, a new version will be issued. 🫢

Oh and also β€” the use of Want is automatically disabled when running under a package that uses overloading. ✨

🌸 __DATA__ and __END__ sections 🌸

okay so bestie, because of a limitation in how source filtering works with Filter::Util::Call, normally you can’t make the data after the __DATA__ or __END__ token available through the special DATA glob. But with Nice::Try, you absolutely can, and it’s giving exactly what you’d expect:

#!/usr/bin/env perl
use strict;
use warnings;
use Nice::Try;

try
{
    print "Poem by Pierre de Ronsard (1545)\n";

    print while( <DATA> );
}
catch($e)
{
    print( "Oh no: $e\n" );
}

__END__
Mignonne, allons voir si la rose
Qui ce matin avoit desclose
Sa robe de pourpre au Soleil,
A point perdu ceste vesprΓ©e
Les plis de sa robe pourprΓ©e,
Et son teint au vostre pareil.

The same would work if the __DATA__ were used instead of __END__ πŸ’–

And if you mix POD, it will ignore it to only make available in the DATA glob the non-POD data. For example:

#!/usr/bin/env perl
use strict;
use warnings;
use Nice::Try;

try
{
    print "Poem by Pierre de Ronsard (1545)\n";

    print while( <DATA> );
}
catch($e)
{
    print( "Oh no: $e\n" );
}

__END__
Mignonne, allons voir si la rose
Qui ce matin avoit desclose
Sa robe de pourpre au Soleil,
A point perdu ceste vesprΓ©e
Les plis de sa robe pourprΓ©e,
Et son teint au vostre pareil.

=encoding utf-8

=head1 NAME

French::Poetry - Pierre de Ronsard, Ode Γ  Cassandre

=head1 DESCRIPTION

This famous poem was made by Pierre de Ronsard after he met Cassandre Salviati, daughter of an Italian banker, at the court in 1545.

This poem is the epitome of Epicureanism (Carpe diem).

=cut

Las ! voyez comme en peu d'espace,
Mignonne, elle a dessus la place,
Las ! las ! ses beautΓ©s laissΓ© choir !
Γ” vraiment marΓ’tre Nature,
Puisqu'une telle fleur ne dure
Que du matin jusques au soir !

Donc, si vous me croyez, mignonne,
Tandis que votre Γ’ge fleuronne
En sa plus verte nouveautΓ©,
Cueillez, cueillez votre jeunesse :
Comme Γ  cette fleur, la vieillesse
Fera ternir votre beautΓ©.

This would yield the entire poem of 3 paragraphs, while skipping the POD in-between. Of course, the same would work with __DATA__ ✨

The DATA is actually an IO::File object generated with Symbol::geniosym() 🫢

See also "Special-Literals" in perldata for more information.


🚧 LIMITATIONS 🚧

Before version v1.3.5, there was a limitation on using signature on a subroutine, but since version v1.3.5, it has been fixed and there is no more any limitation. Thus the following works nicely too.

use strict;
use warnings;
use experimental 'signatures';
use Nice::Try;

sub test { 1 }

sub foo ($f = test()) { 1 }

try {
    my $k = sub ($f = foo()) {}; # <-- this sub routine attribute inside try-catch block used to disrupt Nice::Try and make it fail.
    print( "worked\n" );
}
catch($e) {
    warn "caught: $e";
}

__END__

⚑ PERFORMANCE ⚑

Nice::Try is quite fast, but as with any class implementing a try-catch block, it is of course a bit slower than the natural eval block πŸ’…

Because Nice::Try relies on PPI for parsing the perl code, if your code is very long, there will be an execution time penalty 😭

If you use framework such as mod_perl2, then it will only affect the first time the code is run, since afterward, the code will be loaded in memory.

Still, if you use perl version v5.34 or higher, and have simple need of try-catch, then simply use instead perl experimental implementation, such as:

use v5.34;
use strict;
use warnings;
use feature 'try';
no warnings 'experimental';

try
{
    # do something
}
catch( $e )
{
    # catch fatal error here
}

🐞 DEBUGGING 🐞

And to have Nice::Try 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 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 to:

use Nice::Try no_filter => 1;

to avoid Nice::Try from filtering your script ✨

If you want Nice::Try to produce human readable code, pass it the debug_code parameter like this:

use Nice::Try debug_code => 1;

πŸ›  CLASS FUNCTIONS πŸ› 

The following class functions can be used.

🧩 implement 🧩

my $new_code = Nice::Try->implement( $perl_code );
eval( $new_code );

Provided with a perl code having one or more try-catch blocks and this will return a perl code converted to support try-catch blocks.

This is designed to be used for perl code you store, such as subroutines dynamically loaded or eval'ed.

For example:

my $code = Nice::Try->implement( <<EOT );
sub $method
{
    my \$self = shift( \@_ );
    try
    {
        # doing something that may die here
    }
    catch( \$e )
    {
        return( \$self->error( "Oops: \$e ) );
    }
}
EOT

You can also pass an optional hash or hash reference of options to implement and it will be used to instantiate a new Nice::Try method. The options accepted are the same ones that can be passed when using use Nice::Try.


🧡 THREAD-SAFETY 🧡

Nice::Try is thread-safe for both compile-time and runtime operations. During compilation, source filtering is isolated per thread/process. At runtime, transformed code uses local variables (e.g., $Nice::Try::EXCEPTION, $Nice::Try::DIED) to ensure thread isolation. The finally block, implemented via Nice::Try::ScopeGuard, is thread-safe due to per-object state.

Context awareness with Want is disabled in threaded environments to avoid known issues, reverting to wantarray in perlfunc for safety:

use threads;
use Nice::Try;

try
{
    print "In thread ", threads->tid, "\n";
}
catch( $e )
{
    print "Error: $e\n";
}

πŸ’– CREDITS πŸ’–

Credits to Stephen R. Scaffidi for his implementation of Try::Harder from which I initially borrowed some code.


✍️ AUTHOR ✍️

Jacques Deguest <jack@deguest.jp>


πŸ‘€ SEE ALSO πŸ‘€


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

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


πŸ’– that’s the whole page, bestie! generated from Nice::Try(3), perl v5.42.2, dated 2026-02-15 🫢✨

πŸ“¦ where this page comes from