Use postfix dereferencing

[Update: This feature became stable in Perl v5.24]

Perl v5.20 offers an experimental form of dereferencing. Instead of the complicated way I’ll explain in the moment, the new postfix turns a reference into it’s contents. Since this is a new feature, you need to pull it in with the feature pragma (although this feature in undocumented in the pragma docs) (Item 2. Enable new Perl features when you need them. and turn off the experimental warnings:

use v5.20;
use feature qw(postderef);
no warnings qw(experimental::postderef);

To turn an array reference into its elements, after the arrow -> you use @ with a star, * after it:

my @elements = $array_ref->@*;

This also works with the anonymous array constructor, although you would be wrapping that package just to unwrap it. Sometimes consistent syntax is like that:

my @elements = [ qw(cat bird dog) ]->@*;

In prior versions, you have to use the circumfix notation to do the same thing:

my @elements = @$array_ref;
my @elements = @{ $array_ref };

The new notation is handy for things such as foreach which needs a list:

foreach my $element ( $array_ref->@* ) {
	...
	}

This feature is more interesting when the reference doesn’t come from a variable and you can’t conveniently use the circumfix notation. Perhaps the reference is the return value of a subroutine:

sub some_sub {
	state $ref = [ qw(cat dog bird) ];
	$ref;
	}

foreach my $element ( some_sub()->@* ) {
	...
	}

This works for all of the reference types, although the subroutine version is a bit weird. I’ll get to the subroutine postfix dereference later.

If you use a subscripty thing after the postfix sigil to get a single element of a slice:

my $first = some_sub()->@[0];
my @slice = some_sub()->@[0,-1];

The single element access is not that interesting because you can already do that without the postfix sigil:

my $first = some_sub()->[0];

Here’s an interesting bit of Perl syntax. What happens when you put two indices in that?

my( $first ) = some_sub()->[0, -1];

Perl doesn’t think that’s a slice so it treats the expression inside the braces in scalar context. The comma in scalar context evaluates the left had side, discards the result, evaluates the right hand side, and returns the result. It’s the same thing as the behavior in the FAQ What is the difference between a list and an array?.

In this code, only $first gets a value, but it’s from the last subscript, while $second gets none:

my( $first, $second ) = some_sub()->[ 
	do { say "Evaluated!"; 0 }, 1 
	]; 
say "First is $first";
say "Second is $second";

Perl evaluates the first “subscript”, but doesn’t use it. It uses the last result in the comma chain, even though the assignment looks like it’s in list context.

Wedge that postfix @ in there and both $first and $second get values:

my( $first, $second ) = some_sub()->@[ 
	do { say "Evaluated!"; 0 }, 1 
	]; 
say "First is $first";
say "Second is $second";

Don’t make the common mistake of associating the @ with an array. Just like the array and hash slices, Perl figures out the type with the the subscripty braces.

A postfix hash slice still has the @ and gives back a list of values:

sub get_hashref {
	state $ref = { 
		cat  => 'Buster',
		dog  => 'Addy',
		bird => 'Poppy',
		};
	$ref;
	}

my( $first, $second ) = get_hashref()->@{ qw(cat dog) }; 
say "First is $first";
say "Second is $second";

Curiously, in all of this, the subroutine with the postfix dereference is always called in scalar context despite what you are doing with the result or how you are assigning it. A reference is always a scalar, and that’s a single thing.

Scalar and array interpolation

You can interpolate the postfix dereference notation with scalar and array references if you enable the postderef_qq feature (also undocumented in the pragma docs):

use v5.20;

use feature qw(postderef postderef_qq);
no warnings qw(experimental::postderef);


my $scalarref = \ 'This is the string';
say "Scalar is < $scalarref->$* >";
my $arrayref = [ qw(cat bird dog) ];

say "Array is < $arrayref->@* >";

Notice that without the postderef feature, Perl would try to interpolate the $* (a Perl 4 multi-line feature removed in v5.10).

I’ve only found one useful demonstration of the postfix scalar dereference. Mojo::JSON uses references to the literals 0 and 1 to represent JSON’s true and false. The postfix dereference gets you the boolean value:

use Encode qw(encode);
use Mojo::JSON qw(decode_json);
use Mojo::Util qw(dumper);

my $raw_octets = encode( 'UTF-8',
	'{"name":"Bender","robot":true}' );

my $hash = decode_json( $raw_octets );

say "$hash->{name} is a robot? ",
	$hash->{robot}->$* ? 'Yes' : 'No';

Circumfix notation

To appreciate the ease of the postfix dereference notation, you should understand the circumfix notation which earlier versions use. I cover this in Intermediate Perl, but here’s a short explanation.

Remember the notation for an array variable. There’s a sigil to give some context, an identifier (the name), and possible subscripts:

SIGIL  IDENTIFIER   SUBSCRIPT

In Perl, you can put whitespace between those parts and it still works, including with the new key/value slice syntax:

@ IDENTIFIER
$ IDENTIFIER [ 0 ]
@ IDENTIFIER [ @indices ]
% IDENTIFIER [ @indices ]

% IDENTIFIER
$ IDENTIFIER { 'cat' }
@ IDENTIFIER { @keys }
% IDENTIFIER { @keys } 

You can replace IDENTIFIER with a reference. This is the circumfix notation, called that because there’s stuff around the thing you’re dereferencing. The general syntax uses braces around the reference, which, like in the previous section, might be something that produces a reference and not a variable:

@ { REFERENCE }
$ { REFERENCE } [ 0 ]
@ { REFERENCE } [ @indices ]
% { REFERENCE } [ @indices ]

% { REFERENCE }
$ { REFERENCE } { 'cat' }
@ { REFERENCE } { @keys }
% { REFERENCE } { @keys } 

If the REFERENCE is a simple scalar variable (not a single element access, a subroutine call, or something else), you can omit the braces:

@ $array_ref
$ $array_ref [ 0 ]
@ $array_ref [ @indices ]
% $array_ref [ @indices ]

% $hash_ref
$ $hash_ref { 'cat' }
@ $hash_ref { @keys }
% $hash_ref { @keys } 

Now, suppose you have an array of arrays of arrays of hashes, just to look at something complicated:

my $array = [
	[
		[
			{ animal => 'cat', name => 'Buster' },
		]
		...
	],
	...
	];

To get the first element of the array reference, you can dereference with the circumfix notation. This gives you the first array reference under the top level reference, leaving off the braces because the top level is in a simple scalar variable:

$array_ref[0]

To get the first item from that reference, you have to use the braces since that’s a not simple scalar variable:

${ $array_ref[0] }[0]

To get all the elements from that reference, you add more braces:

@{ ${ $array_ref[0] }[0] }

It’s the same with the arrow notation (even implied!) which makes the inside only slightly prettier but still needs the outside braces:

@{ $array_ref->[0]->[0] }

You can leave off the arrows between subscripts:

@{ $array_ref->[0][0] }

It’s no wonder some people think Perl is ugly.

The postfix dereferencing looks nicer because it doesn’t use the braces, but it also keeps everything in order from left to right:

$array_ref->[0][0]->@*

You need that extra arrow at the end; it’s a syntax error otherwise:

$array_ref->[0][0]@*        # syntax error!

Subroutine postfix dereference

The postfix dereference for a subroutine is slightly odd because there are different ways that you can call a subroutine, but the postfix dereference uses one of the uncommon ones.

Start with a named subroutine. With just the & and no parentheses, the called subroutine gets the arguments in the current version of @_:

local @_ = qw(Buster Mimi Ginger);

sub some_sub { "@_" }

say "With &: ", &some_sub;     # With &: Buster Mimi Ginger
say "With &(): ", &some_sub(); # With &():

The postfix dereference form has the same behavior. It executes the subroutine reference with the current @_. Dereferencing the sub reference with ->() is different; it specifies an empty argument list:

use v5.20;

use feature qw(postderef);
no warnings qw(experimental::postderef);

my $sub = sub { "@_" };

local @_ = qw(Buster Mimi Ginger);
say $sub->&*;   # Buster Mimi Ginger
say $sub->();   #

Things to remember

  • Perl v5.20 adds a postfix notation to dereference all variable types
  • Subroutines returning references are called in scalar context
  • Subroutine references dereferenced with the postfix notation use the current value of @_

7 thoughts on “Use postfix dereferencing”

  1. That some_sub()->[0, -1] is not the only place where the scalar comma operator trips people up. The useful applications of this operator are far outweighed by the cases where it gets in the way. Is it possible to make perl give a warning or syntax error when it encounters the scalar comma operator?

  2. Nice, though in the circumfix examples, you left off the reasonably attractive:

    @{ $$array_ref[0][0] }
    

    Which is my current preferred and isn’t all that bad.

    My only beef with the new syntax is that it moves the expression type (list or scalar) to the end of the expression rather than the front where we are used to looking for it.

  3. I can’t see how replacing three chars @{} with four chars ->@* makes things better or easier. Am I missing something? Although it might seem better to have all the dereferencing stuff in one place (behind), I still don’t think this outweighs the extra character.

  4. It means that you can write things as a chain. You can write the access from left to right as you think of it (such as turning a reference into a list of its elements) rather than adding characters to both sides of the expression. If you think that an extra character is the burdensome thing here, you might consider learning how to type. Just as we read entire words or phrases at once, when your fingers get used to typing phrases you don’t think about individual characters.

    And, for what it is worth, I tend to put spaces inside @{} so the interior expression stands out. That means the new syntax saves me a character.

  5. The chaining argument would make more sense if you could more easily apply additional actions to the chain. For example, if after derefing an array ref, you wanted to sort the list and take the first element, you’d still be in the same situation where you have to use something like circumfix notation:

    $scalar = (sort {$a <=> $b} $aref->@*)[0]
    
  6. It currently does,

    use warnings;
    my $foo = [qw/foo bar baz quz quuz quuuz/];
    say $foo->[2,4];
    Useless use of a constant (2) in void context at trash.pl line 10.

  7. > My only beef with the new syntax is that it moves the expression type (list or scalar) to the end of the expression rather than the front where we are used to looking for it.

    This is confusing to me either.

Comments are closed.