In a recent post, I said I wished that Perl's built in functions for array containers would work directly on references.
Rather than this (today):
# Assuming $data->{$key1}{$key2} = [ qw/foo bar/ ]
push @{ $data->{$key1}{$key2} }, @stuff;
I wanted this (in the future):
# Assuming $data->{$key1}{$key2} = [ qw/foo bar/ ]
push $data->{$key1}{$key2}, @stuff;
I've finished a draft implementation that works for push, pop, shift, unshift and splice. All existing Perl tests pass, as do new tests I've written that explore this new functionality. It will even auto-vivify as needed:
my $foo; push $foo, @stuff; # $foo is now an arrayref
I'm still working on keys, values and each and those look much harder, but I hope to have something to share by next week.
Updated: I added example data to the "given $data..." comments to clarify that it's an example, not a recommendation about how to initialize a data structure.
17 Comments
Hi
Are you sure you want this? I don't.
That is - I'd like to know I can turn this 'feature' on and off, and hence off.
And why is that?
Because I rely on my code failing with an error msg if I push onto something which I have not previously initialized explicitly as an array or array ref. In other words my gut reaction is that this looks suspiciously like a bad idea 'which seemed like a good idea at the time'.
Also, if it were optional, how would it tie in with common::sense?
Things to think about :-).
Cheers
PS The vertical text in the captcha is almost unreadable.
Perl already does autovivification if you explicitly dereference an undefined value:
my $foo; push @$foo, @stuff;If you really don't like that behavior (and I respect that point of view), you might be interested in Vincent Pit's autovivification module, which lets you say
no autovivificationto disable it in a lexical scope.Ron, it's no different than the way hashes auto-vivify now. In fact this is now more consistent since it's not just hashes that get the special treatment. Also, if you don't like the feature, don't use it. If you use the same syntax we have now (explicitly dereferencing arrayrefs as arrays) then it will behave as you intend and you'll get an error about something not being an array.
But I'm all in favor of this. Dereferencing references has always been an ugly spot in Perl's syntax and this goes a long want to get rid of code that's unnecessary.
Doesn't autobox give you something like
$data->{$key1}{$key2}->push(@stuff);?
Yes, with the overhead of loading autobox and doing method resolution and dispatch. My work modifies the op-tree at compile time so that
push $foo, @stuffwinds up with the exact same ops as the more explicitpush @$foo, @stuff. There is no additional overhead.autobox is a great tool, but it's also a general purpose one. My work is a very focused. The two can (and should) co-exist for different needs.
I think Ron's reaction might have been somewhat similar to my own, as a result of the example:
push $foo, @stuffThe problem with that is that some of us sometimes type exactly that code when we really mean to push
$fooonto@stuff. My first reaction was exactly Ron's, until I realized that in those cases$foowill never be unintialized, so everything will work as needed for everyone so long as trying topushonto an initialized scalar will die, but pushing onto an array ref will succeed and pushing onto an unitialized scalar will autovivify an array ref.Rather confusing.
Assuming $data->{$key1}{$key2} = []
push @{ $data->{$key1}{$key2} }, @stuff;
What about this ?
$data->{$key1}{$key2} = [ @stuff ];
My apologies for the bad example. I just meant to say that given some deep data structure with an arrayref at the end, you can push onto it without an explicit dereference. It could have been
$data->{$key1}{$key2} = [ qw/a b c/ ]; push $data->{$key1}{$key2}, qw/d e f/;In fact, I'll go change it now
This would be a great benefit to perl source code readability imho.
I hope it will make its way to the core some day.
@aero:
for initializations of arrays, push isn't a good option anyway, just for on-the-fly init in loops or the like.
Assuming $data->{$key1}{$key2} is hashref.
my ($k,$v) = each %{ $data->{$key1}{$key2} }
also should be modified? to be able to do like this
my ($k,$v) = each $data->{$key1}{$key2}
I'm exploring whether that is possible. The internals of each() are much more complicated, in part due to the fact that each/keys/values now support arrays as well as hashes.
Don't forget tests for the cases where the hash value is a non-reference, or a reference of a different sort. How are you going to report the errors in those cases, etc? I've found that in new features the perl tests tend to forget many edge cases. :(
As I replied to theory, internally, the push/pop work rewrites the op tree to make
push $foo, @stuffequivalent topush @{ $foo }, @stuff. So at that point, all the same protections for an invalid$fooare in place.Doing the same smart dereferencing for each, keys and values looks to be hard because it can't be done just with the op-tree (unless this feature were limited only to hashrefs, which would be confusing and suboptimal). Getting all the edge and corner cases right is tricky. (E.g. a blessed scalar reference that overloads
%{})Hopefully this functionality will require 'use 5.14;' (or whatever version this might appear in), so people using 5.12 and earlier don't get bit by un-constrained use of this (not really necessary) feature in modules and code that doesn't otherwise express its perl version requirements. Unfortunately, that doesn't seem a priority, e.g. see each / keys /values supporting arrays.
It would be a syntax error on previous versions of perl, so it does not require protection by feature.pm (whether or not it would be enabled by 'use 5.14'). I agree with the practice of listing the minimum version of perl supported in modules. I recommend using perlver to detect minimum versions based on syntax. I use Dist::Zilla::Plugin::MinimumPerl in my release tools to automate such checks.
The happiness will come when it will be possible to get rid of brackets in the code.
For example instead of
if (an eq b) {
c=d;
}
It will be possible to write
if an eq b then c=d;
I've often wondered why perl isn't smart enough to do that.
Hopefully it will be accepted for 5.14!
3 Trackbacks
[...] fiddling with Perl’s guts to have the core array functions (push, pop, shift and friends) to DWIM on array references. His end-goal is to be able to do without the noisome array de-referencing in situations like [...]
[...] week, I wrote about hacking push and pop to take array references as well as literal arrays. This week, I've added similar functionality for [...]
[...] my work on push() and keys(), I want to be able to represent a prototype that takes an array or an array reference or [...]