New Block Operations
I'd say the ability to expand the language, creating pseudo block operations is one.
You declare the prototype for a sub indicating that it takes a code reference first:
sub do_stuff_with_a_hash (&\%) {
my ( $block_of_code, $hash_ref ) = @_;
while ( my ( $k, $v ) = each %$hash_ref ) {
$block_of_code->( $k, $v );
}
}
You can then call it in the body like so
use Data::Dumper;
do_stuff_with_a_hash {
local $Data::Dumper::Terse = 1;
my ( $k, $v ) = @_;
say qq(Hey, the key is "$k"!);
say sprintf qq(Hey, the value is "%v"!), Dumper( $v );
} %stuff_for
;
(Data::Dumper::Dumper
is another semi-hidden gem.) Notice how you don't need the sub
keyword in front of the block, or the comma before the hash. It ends up looking a lot like: map { } @list
Source Filters
Also, there are source filters. Where Perl will pass you the code so you can manipulate it. Both this, and the block operations, are pretty much don't-try-this-at-home type of things.
I have done some neat things with source filters, for example like creating a very simple language to check the time, allowing short Perl one-liners for some decision making:
perl -MLib::DB -MLib::TL -e 'run_expensive_database_delete() if $hour_of_day < AM_7';
Lib::TL
would just scan for both the "variables" and the constants, create them and substitute them as needed.
Again, source filters can be messy, but are powerful. But they can mess debuggers up something terrible--and even warnings can be printed with the wrong line numbers. I stopped using Damian's Switch because the debugger would lose all ability to tell me where I really was. But I've found that you can minimize the damage by modifying small sections of code, keeping them on the same line.
Signal Hooks
It's often enough done, but it's not all that obvious. Here's a die handler that piggy backs on the old one.
my $old_die_handler = $SIG{__DIE__};
$SIG{__DIE__}
= sub { say q(Hey! I'm DYIN' over here!); goto &$old_die_handler; }
;
That means whenever some other module in the code wants to die, they gotta come to you (unless someone else does a destructive overwrite on $SIG{__DIE__}
). And you can be notified that somebody things something is an error.
Of course, for enough things you can just use an END { }
block, if all you want to do is clean up.
overload::constant
You can inspect literals of a certain type in packages that include your module. For example, if you use this in your import
sub:
overload::constant
integer => sub {
my $lit = shift;
return $lit > 2_000_000_000 ? Math::BigInt->new( $lit ) : $lit
};
it will mean that every integer greater than 2 billion in the calling packages will get changed to a Math::BigInt
object. (See overload::constant).
Grouped Integer Literals
While we're at it. Perl allows you to break up large numbers into groups of three digits and still get a parsable integer out of it. Note 2_000_000_000
above for 2 billion.