Use array references with the array operators

There’s a significant change in syntax showing up in Perl 5.14. The array operators push, pop, shift, and unshift previously only worked on named arrays or dereferenced references. Now, thanks to David Golden, they’ll work on array references. Not only that, they’ll work on references that you’ve stored in variables or that come as the return values from subroutine calls. This feature will show up in Perl 5.13.7, so you need to compile a development version to try this:

my $ref = [ qw(Buster Mimi Ginger Ella) ];

sub get_dogs { [ qw(Nicki Addy) ] }

push $ref, 'Addy';
print "Pets are @$ref\n";

my $dog = shift get_dogs();
print "Dog is $dog\n";

This brings some Perl 6 ideas into Perl 5: where you can use an array, you can use things that act like arrays. This blurs the difference between a named array and a reference to it so Perl has even more DWIMery.

As of today, you have to compile the latest version of blead to try out this feature:

$ blead arrays2.pl
Pets are Buster Mimi Ginger Ella Addy
Dog is Nicki

Prior to Perl 5.14, perl would catch these errors at compile-time and tell you what it found and what it expected:

% perl5.12.2 array_operators.pl
Type of arg 1 to push must be array (not private variable) at arrays.pl line 5, near "'Addy';"
Type of arg 1 to shift must be array (not subroutine entry) at arrays.pl line 8, near ");"
Execution of arrays.pl aborted due to compilation errors.

The fix to this, you have to use the ungainly deferencing syntax:

my $ref = [ qw(Buster Mimi Ginger Ella) ];

sub get_dogs { [ qw(Nicki Addy) ] }

push @$ref, 'Addy';
print "Pets are @$ref\n";

my $dog = shift @{ get_dogs() };
print "Dog is $dog\n";

It’s ugly, but it works:

$ perl5.12.2 arrays2.pl
Pets are Buster Mimi Ginger Ella Addy
Dog is Nicki

If you want to get rid of the uglies, get ready to upgrade to Perl 5.14.

Post to Twitter Post to Delicious Post to Digg Post to Facebook Post to Reddit

Leave a comment

6 Comments.

  1. Thank you for your helpful posting, always.

    Is this change applied to other array operators, like “sort”, “reverse”, “splice”, etc. ?

    • I know that splice is also fixed, but I’m not sure about the others (I guess I could just try it and find out :) reverse() is a tougher nut because it is not just an array operator, although Larry once told me that he can’t remember why he thought a scalar version was ever a good idea.

      Here’s the relevant entry from the the perl git log:

      % git log cba5a3
      commit cba5a3b05660d6a40525beb667a389a690900298
      Author: David Golden 
      Date:   Thu Sep 9 17:22:02 2010 -0400
      
          Allow push/pop/keys/etc to act on references
      
          All built-in functions that operate directly on array or hash
          containers now also accept hard references to arrays or hashes:
      
            |----------------------------+---------------------------|
            | Traditional syntax         | Terse syntax              |
            |----------------------------+---------------------------|
            | push @$arrayref, @stuff    | push $arrayref, @stuff    |
            | unshift @$arrayref, @stuff | unshift $arrayref, @stuff |
            | pop @$arrayref             | pop $arrayref             |
            | shift @$arrayref           | shift $arrayref           |
            | splice @$arrayref, 0, 2    | splice $arrayref, 0, 2    |
            | keys %$hashref             | keys $hashref             |
            | keys @$arrayref            | keys $arrayref            |
            | values %$hashref           | values $hashref           |
            | values @$arrayref          | values $arrayref          |
            | ($k,$v) = each %$hashref   | ($k,$v) = each $hashref   |
            | ($k,$v) = each @$arrayref  | ($k,$v) = each $arrayref  |
            |----------------------------+---------------------------|
      
      .... and much more ....
      
  2. I think it would be sweet to teach map and grep to accept an arrayref and return an arrayref. So:

    my $out = [ map { uc $_ } @$in ];
    

    becomes

    my $out = map { uc $_ } $in;
    
    • The problem is that map and grep operate on lists, whereas push, pop, etc., operate on arrays or dereferenced arrayrefs. An array may be used when a list is expected but not the other way around. It’s relatively easy to allow arrayrefs when you previously only accepted arrays. However, a list passed to map or grep could be a list of arrayrefs and it might only have one element: a single arrayref. In that case, auto-dereferencing would iterate over a value that was never intended to be used in that way, causing errors that would be hard to catch.

  3. Just wondering: when $ref is an arrayref, will

    my $count = scalar $ref;
    

    be the same as

    my $count = @$ref ?
    
  4. The scalar() built-in isn’t an array operator, so this doesn’t apply to it. However, you can always just try it yourself and find out, as well as checking the documentation for scalar(). :)

Leave a Reply

You must be logged in to post a comment.