http://qs1969.pair.com?node_id=455749

This meditation is a story about how the aliasing semantics of perl has caused me a hard-to-find bug. This is not meant to prove that aliasing is useless, I know how useful it can be in other circumstances.

Suppose I have an array of intermediate results (the numbers below are for illustration only):

my @root = (1022, 2);
While debugging a program, I decided to dump the array at one point of the program. However, I thought that the array could get too large, so I decided to print only the first eight elements. I knew that all the elements of the array were defined, so I decided to do this:
$verbose and warn "found " . @root . " root nodes: " . join(" ", grep +{ defined } @root[0 .. 7]);
Unexpectedly, this caused a different error in the program, resulting in these messages:
Use of uninitialized value in hash element at ./rock-cut line 163. Can't use an undefined value as an ARRAY reference at ./rock-cut line +171.
It took me quite a lot of time to figure out what was wrong. The program wasn't running very fast, so I had to wait for its completion after each change. I didn't try to run it with DB, because that would make the program about 4 times slower.

After a while, I figured out that if I change the line to this, the bug disappears:

$verbose and warn "found " . @root . " root nodes: " . join(" ", @root +[0 .. (@root < 8 ? @root : 8) - 1]);

I was very surprised, because I wouldn't have thought this change could make any difference. But the fact is that the first version has changed the array.

Recall that @root had two elements. This expression

grep { defined } @root[0 .. 7]
has made the elements @root[2 .. 7] spring into existsance, thus the array has grown to 8 long. This has caused a problem later, when I iterated through @root and tried to use the undefined elements as a hash key.

Now just by saying () = @root[0 .. 7] does not change the length of the array, it just returns undefined values where the elements do not exist. So what's happened here?

Well, the answer is that grep wants to alias the elements of the list to $_ so perl must create a scalar for them.

The same thing can happen with an array element instead of a range, or a hash slice or element. It does not happen with the print operator instead of grep It can also happen with user-defined subroutine instead of print.

There are still issues here I don't understand. I'd be glad if someone could shed some light on how come $a[1] does not exist in the following test program:

#!perl use warnings; no warnings "uninitialized"; use Dumpvalue; $D = Dumpvalue->new; sub f { print $_[0]; } sub g { $_[0] = 5; } f($a[1]); f(@a[2, 3]); g($a[5]); g(@a[6, 7]); print $a[9]; print @a[10, 11]; () = grep 0, $a[13]; () = grep 0, @a[14 +, 15]; $t = $a[17]; @t = @a[18, 19]; f($h{1}); f(@h{2, 3}); g($h{5}); g(@h{6, 7}); print $h{9}; print $h{10, 11}; () = grep 0, $h{13}; () = grep 0, @h{14 +, 15}; $t = $h{17}; @t = @h{18, 19}; $D->dumpValues(\@a, \%h); __END__
Output:
0 ARRAY(0x81495c0) 0 empty slot 1 empty slot 2 undef 3 undef 4 empty slot 5 5 6 5 7 undef 8 empty slot 9 empty slot 10 empty slot 11 empty slot 12 empty slot 13 undef 14 undef 15 undef 1 HASH(0x8192464) 13 => undef 14 => undef 15 => undef 2 => undef 3 => undef 5 => 5 6 => 5 7 => undef

Updates: changed code from one-liner to perl -x script, per suggestion of diotalevi. Also added some more readmore tags now that this is frontpaged.

Let me note that I think this is not autovivification, and I tried to explain why in a reply although my explanation isn't very clear.

Replies are listed 'Best First'.
Re: Aliasing bites
by revdiablo (Prior) on May 10, 2005 at 22:10 UTC

    Correct me if I'm wrong, but isn't this more about autovivification than it is about aliasing? Your leading paragraph sounds about right, that aliasing can lead to hard to debug problems. But the meat of your Meditation appears to be an autovivification problem, to my eyes.

    Update: I see where the aliasing comes in, but I still think the problem is caused by autovivification. It's all a matter of perspective, really, and I just wanted to explain mine. I suppose this is ambrus' Meditation, and he can say the topic is aliasing if he wants, but that doesn't change my view of the particular example he chose to use.

      I disagree, this is not an autovivification problem.

      Autovivification happens when you have an undefined scalar, you dereference it in an lvalue context, then a new scalar/array/hash gets allocated, and the formerly undefined scalar is set to a reference to this new scalar/array/hash.

      The simplest example to autovivification is this: my $x; $$x = 42; After this, the content of $x is no longer undef, but instead, it's a reference to 42. Similarly, any of these create a new array and sets the scalar to a reference to it:

      my $x; @$x = (5, 7); my $y; $$y[2] = 9; my $z; @$z[2 .. 4] = (5, 7); my $w; grep 0, @$w[2 .. 4];

      Now compare this to my example, which was, in its simplest form:

      my @x; grep 0, $x[2];
      This has not set any (formerly undefined) scalar to a reference. While it has allocated a scalar, that's for a different reason.
      It is an autovivification problem, yes. However, the autovivification occurs due to the aliasing of grep (and map and sort and a few others). That's why aliasing is correctly the focus of this meditation.

      • In general, if you think something isn't in Perl, try it out, because it usually is. :-)
      • "What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against?"
Re: Aliasing bites
by Zaxo (Archbishop) on May 11, 2005 at 07:44 UTC

    On aliases, I wish Perl had more of 'em. This doesn't really have a lot to do with your meditation, but the topic made it spill out.

    I'd like to have lexical variables which are aliases to other values. Currently, you only get that by defining a for loop variable:

    my ($foo,$bar,$baz,$i); for my $quux ($foo, $bar, $baz) { $quux = $i++; } print "$foo $bar $baz\n"; __END__ 0 1 2
    I think that explicit aliasing which gives value semantics to references would be very desirable. A new alias keyword taking $_ as default argument would allow, for instance, named aliases for subroutine arguments:
    sub wibble { return unless @_ > 2; my ($string, $position, $count) = map { alias } @_; # . . . }
    where you wish to modify the external arguments.

    I expect this sort of thing has been discussed before.

    After Compline,
    Zaxo

      I'd like to have lexical variables which are aliases to other values.

      Luckily, there's Perl 6 :)

      my $var = ...; my $alias := $var; # That's it. # Or: my $short := %alias_to<a_very>[long()]<name>; $short = ...; # Modifies %alias_to

      --Ingo

      While I understand that perl6 will have this feature, I belive there will be less need for it because of the other changes in the language.

      The times I feel the need of lexical aliases (in perl5) most are nested datastructures and lexical subs.

      For example, when I have a ref-to-hash $h, I can only access elements like $$h{"foo"} (or the arrow notation), which is ugly. It would be much simpler to alias it to %h and then I could just access the elements as $h{"foo"}. Of course, you can make a copy like my %h = %$h;, but that can be slow, and you have to copy it back with %$h = %h; if you modifiy %h. In perl6, however, $$h{"foo"} will become $h{"foo"}, which is a simple enough notation so the problem will be gone. (As a sidenote, there are nonstandard modules for perl5 that allow lexical aliasing.)

      Lexical subs are very similar. Today, the way to create lexical subs is my $frobnicate = sub { ... }; and we call them by &$frobnicate(@args) (or the arrow notation), which is, again, ugly. In perl6, this shall simplify to $frobnicate(@args).

      Of course, perl6 will have lexical aliases like my %h := %$h and true lexical &-variables in addition to $, @, % so you can use aliasing to solve either of these too. TIMTOWTDI.

      Update: lexical aliases wouldn't solve my second problem. We'd need lexical subs for that.

        Perl6 also won't need the arrow notation to dereference values:

        my %hash = (a => 1, b => 2); my $href = \%hash; say %hash<a>++; # prints 1 say $href<a>; # prints 2

        Update: noticed ambrus already mentioned that. Sorry for the repitition. :-)

      Perhaps I've missunderstood your post, but what do aliases get you that references don't? Slightly nicer syntax?

        Compare the output from these two snippets:

        $ pugs -e 'my %hash; %hash<foo>[0]<bar>[0] = 1; my $alias := %hash<foo>[0]; say ref $alias; %hash<foo>[0] = 5; say ref $alias' Hash Int $ pugs -e 'my %hash; %hash<foo>[0]<bar>[0] = 1; my $ref = %hash<foo>[0]; say ref $ref; %hash<foo>[0] = 5; say ref $ref' Hash Hash