2023 twenty-four merry days of Perl Feed

My Top 7 Perl New Features

perl - 2023-12-12

For each version of Perl, I update my book Perl New Features. Perl v5.10 has a big change in perl development where we started to get new syntax instead of making old syntax adapt to the new world (like making things Unicode).

There are so many features that I could choose from, but I looked back at those I've been using for the past year, and which ones I'd fight for in a code review. These aren't the most clever or groundbreaking features, but they are the ones that I'm consistently using.

0. Subroutine signatures

Perl v5.20 added experimental signature support, and v5.36 moves these out of experimental status:

use v5.36;

sub do_it {
    my( $name, $cost, $age, $limit ) = @_;
    ...
}

It seems like a slight change, but moving the names for the arguments outside of the braces makes everything much tidier:

use v5.36;

sub do_it ( $name, $cost, $age, $limit ) {
    ...
}

Some of these can have default values (all required values must come first):

use v5.36;

sub do_it ( $name, $cost, $where, $limit = 5 ) {
    ...
}

do_it( 'brian', 3, 'NYC' ); # limit is the default

And, Perl v5.38 lets me use the defined-or to set default values when I use undef for a placeholder:

use v5.38;

sub do_it ( $name, $cost, $where //= 'NYC', $limit = 5 ) {
    ...
}

do_it( 'brian', 3, 'NYC' ); # limit is the default

do_it( 'brian', 3, undef, 7 ); # where is the default

Eventually, signatures will turn off @_ inside the braces, which has been annoying, but if that's the trade-off for signatures, so be it.

1. Substitution returns a copy.

Perl v5.14 added the the r flag for the substitution operator. When I use that, the bound string is unmodified but I get the modified version with the replacement:

my $modified = $original =~ s/PATTERN/REPLACEMENT/r;

This is especially handy in a map, where I have often made the mistake of transforming @original into a list of 1s or empty strings:

my @modified = map { s/PATTERN/REPLACEMENT/r } @original;

2. Postfix dereference

Perl's generalized dereferencing is a circumfix operator, which is a fancy way of saying that I have to type on both sides of the reference:

	# @{ REFERENCE }
 # @{ $hash{$key} }

foreach my $item ( @{ $hash{$key} } ) {
    ...
}

Perl v5.20 added the postfix dereference feature, and Perl v5.24 stabilized it:

use v5.24;

foreach my $item ( $hash{$key}->@* ) {
    ...
}

3. The infix isa

Checking that a Perl object was of a particular class, or a subclass or that, was a pain. There's isa from UNIVERSAL, but that only works if you have an object. I do this quite a bit in testing Perl code, and since it might blow up, I have to wrap that in an eval:

if( eval { $obj->isa($target_class) } ) {
    ...
}

Perl v5.32 introduced the infix version of isa that can return false if the left-hand side is not an object. This is much more pleasing:

if( $obj isa $target_class ) {
    ...
}

Someday we might get infix operators for can and does.

4. Key-value slices

I'm a big fan of slices, but for a long time (since forever), Perl didn't have a way to slice a hash and get a hash back. For example, I want a sub-hash that I can use a map. I've typed out something like this so often I don't even feel the pain anymore:

my %slice = map { $_ => $hash{$_} } @wanted_keys;

Perl v5.20 introduced "key-value" slices. I know it's a key-value slice because I see the % in front of hash:

use v5.20;
my %key_value_slice = %hash{@wanted_keys};

A hash slice with @ gives you back only the values:

my @value_slice = @hash{@wanted_keys};

In the last year I'm been particularly keen to clean up areas where I'm passing around too much data. Instead of using a "god" hash with lots of irrelevant keys, I extract just the parts I need so that nothing gets more than it should be allowed to use:

use v5.20;
my %sub_hash = %hash{@wanted_keys};
some_function( \%sub_hash );

If I'm really concerned about something changing something they shouldn't, I might make a deep copy:

use v5.20;
use Storable;

my %sub_hash = %hash{@wanted_keys};
my $clone = Storable::dclone( \%sub_hash );
some_function( $sub_hash );

5. Initialize hashes and arrays with state

I absolutely love state and I try to put everything that matters to a subroutine inside its lexical scope. This makes it easy to move the entire subroutine and everything it needs to another place in the file, or even another file.

And, while I'm very comfortable with references, if I can leave out all the dereferencing arrows, ->, I will. When Perl v5.10 added state, it could only initialize scalars. This wasn't a big deal with anonymous arrays, it feels clunky with a hash because I often want to construct the hash to check existence of some allowed value:

sub do_it {
    state $scalar = 137;
    state $array = [ 1, 3, 7 ];
    state $hash = { map { $_, 1 } (1, 3, 7) };

    if( exists $hash->{$args[0]} ) { ... }
}

With v5.28, I can initialize array and hash variables in state:

sub do_it {
    state $scalar = 137;
    state @array = qw( 1, 3, 7 );
    state %hash = map { $_, 1 } (1, 3, 7);

    if( exists $hash{$args[0]} ) { ... } # leave off the ->
}

6. Indented here docs

I love heredocs for multi-line strings, and up to Perl v5.26 you either had to start each line at the left margin, or do clunky post-processing to remove leading whitespace:

if( ... ) {
    my $string = <<'HERE';
This string is flush left!
Here's another line!
This is insane!
HERE

    ...
}

With Perl v5.26, Perl can strip leading whitespace for you, up to the whitespace ahead of the closing delimiter (HERE in this example). Now my heredocs don't have to screw up my indention:

if( ... ) {
    my $string = <<~'HERE';
This string is outdented!
        Here's another line!
        This is nicer!
HERE

    ...
}

Further reading

You can read more about new features for free on The Effective Perler, or you can support my writing by buying the ePub or PDF version of Perl New Features at LeanPub.

Gravatar Image This article contributed by: brian d foy <briandfoy@pobox.com>