NAME¶
Data::Alias - Comprehensive set of aliasing operations
SYNOPSIS¶
use Data::Alias;
alias {
# aliasing instead of copying whenever possible
};
alias $x = $y; # alias $x to $y
alias @x = @y; # alias @x to @y
alias $x[0] = $y; # similar for array and hash elements
alias push @x, $y; # push alias to $y onto @x
$x = alias [ $y, $z ]; # construct array of aliases
alias my ($x, $y) = @_; # named aliases to arguments
alias { ($x, $y) = ($y, $x) }; # swap $x and $y
alias { my @t = @x; @x = @y; @y = @t }; # swap @x and @y
use Data::Alias qw/ alias copy /;
alias { copy $x = $y }; # force copying inside alias-BLOCK
use Data::Alias qw/ deref /;
my @refs = (\$x, \@y, \%z);
foo(deref @refs) # same as foo($x, @y, %z)
DESCRIPTION¶
Aliasing is the phenomenon where two different expressions actually refer to the
same thing. Modifying one will modify the other, and if you take a reference
to both, the two values are the same.
Aliasing occurs in Perl for example in for-loops and sub-calls:
for $var ($x) {
# here $var is an alias to $x
}
foo($y);
sub foo {
# here $_[0] is an alias to $y
}
Data::Alias is a module that allows you to apply "aliasing semantics"
to a section of code, causing aliases to be made whereever Perl would normally
make copies instead. You can use this to improve efficiency and readability,
when compared to using references.
The exact details of aliasing semantics are below under "DETAILS".
SYNTAX¶
alias EXPR | alias BLOCK¶
Exported by default.
Enables aliasing semantics within the expression or block. Returns an alias to
the expression, or the block's return value.
"alias" is context-transparent, meaning that whichever context it is
placed in (list, scalar, void), the expression/block is evaluated in the same
context.
copy EXPR | copy BLOCK¶
Restores normal (copying) semantics within the expression or block, and makes a
copy of the result value (unless in void context).
Like "alias", "copy" is context-transparent.
deref LIST¶
Accepts a list of references to scalars, arrays, or hashes. Applies the
applicable dereferencing operator to each. This means that:
deref $scalarref, $arrayref, $hashref
behaves like:
$$scalarref, @$arrayref, %$hashref
EXAMPLES¶
A common usage of aliasing is to make an abbreviation for an expression, to
avoid having to repeat that (possibly verbose or ugly) expression over and
over:
alias my $fi = $self->{FrobnitzIndex};
$fi = $fi > 0 ? $fi - $adj : $fi + $adj;
sub rc4 {
alias my ($i, $j, $S) = @_;
my $a = $S->[($i += 1) &= 255];
my $b = $S->[($j += $S->[$i]) &= 255];
$S->[(($S->[$j] = $a) + ($S->[$i] = $b)) & 255]
}
In the second example, the rc4 function updates its first two arguments (two
state values) in addition to returning a value.
Aliasing can also be used to avoid copying big strings. This example would work
fine without "alias" but would be much slower when passed a big
string:
sub middlesection ($) {
alias my $s = shift;
substr $s, length($s)/4, length($s)/2
}
You can also apply aliasing semantics to an entire block. Here this is used to
swap two arrays in O(1) time:
alias {
my @temp = @x;
@x = @y;
@y = @temp;
};
The "copy" function is typically used to temporarily reinstate normal
semantics, but can also be used to explicitly copy a value when perl would
normally not do so:
my $ref = \copy $x;
DETAILS¶
This section describes exactly what the aliasing semantics are of operations.
Anything not listed below has unaltered behaviour.
- scalar assignment to variable or element.
- Makes the left-side of the assignment an alias to the
right-side expression, which can be anything.
alias my $lexvar = $foo;
alias $pkgvar = $foo;
alias $array[$i] = $foo;
alias $hash{$k} = $foo;
An attempt to do alias-assignment to an element of a tied (or
"magical") array or hash will result in a "Can't put alias
into tied array/hash" error.
- scalar assignment to dereference
- If $ref is a reference or undef, this simply does
"$ref = \$foo". Otherwise, the indicated package variable (via
glob or symbolic reference) is made an alias to the right-side expression.
alias $$ref = $foo;
- scalar assignment to glob
- Works mostly the same as normal glob-assignment, however it
does not set the import-flag. (If you don't know what this means, you
probably don't care)
alias *glob = $reference;
- scalar assignment to anything else
- Not supported.
alias substr(...) = $foo; # ERROR!
alias lvalsub() = $foo; # ERROR!
- conditional scalar assignment
- Here $var (and $var2) are aliased to $foo if the applicable
condition is satisfied. $bool and $foo can be any expression. $var and
$var2 can be anything that is valid on the left-side of an
alias-assignment.
alias $bool ? $var : $var2 = $foo;
alias $var &&= $foo;
alias $var ||= $foo;
alias $var //= $foo; # (perl 5.9.x or later)
- whole aggregate assignment
- These alias entire aggregates (arrays or hashes), not
merely their contents. This means for example that "\@lexarray ==
\@foo".
alias my @lexarray = @foo;
alias my %lexhash = %foo;
alias @pkgarray = @foo;
alias %pkghash = %foo;
Making the left-side a dereference is also supported:
alias @$ref = @foo;
alias %$ref = %foo;
and analogously to assignment to scalar dereference, these will change $ref
to reference the aggregate, if $ref was undef or already a reference. If
$ref is a string or glob, the corresponding package variable is aliased.
If the right-side expression is not an aggregate (of the same type), then a
new one is implicitly constructed. This means:
alias my @array = ($x, $y, $z);
alias my %hash = (x => $x, y => $y);
is translated to:
alias my @array = @{ [$x, $y, $z] };
alias my %hash = %{ {x => $x, y => $y} };
If you want to merely replace the contents of the aggregate with aliases to
the contents of another aggregate, but not alias the aggregates
themselves, you can force list-assignment by parenthesizing the left side,
see below.
- list assignment
- List assignment is any assignment where the left-side is an
array-slice, hash-slice, or list in parentheses. This behaves essentially
like many scalar assignments in parallel.
alias my (@array) = ($x, $y, $z);
alias my (%hash) = (x => $x, y => $y);
alias my ($x, $y, @rest) = @_;
alias @x[0, 1] = @x[1, 0];
Any scalars that appear on the left side must be valid targets for scalar
assignment. When an array or hash appears on the left side, normally as
the last item, its contents are replaced by the list of all remaining
right-side elements. "undef" can also appear on the left side to
skip one corresponding item in the right-side list.
Beware when putting a parenthesized list on the left side. Just like Perl
parses "print (1+2)*10" as "(print(1+2))*10", it would
parse "alias ($x, $y) = ($y, $x)" as "(alias($x, $y)) =
($y, $x)" which does not do any aliasing, and results in the
"Useless use of alias" warning, if warnings are enabled.
To circumvent this issue, you can either one of the following:
alias +($x, $y) = ($y, $x);
alias { ($x, $y) = ($y, $x) };
- Anonymous aggregate constructors
- Return a reference to a new anonymous array or hash,
populated with aliases. This means that for example
"\$hashref->{x} == \$x".
my $arrayref = alias [$x, $y, $z];
my $hashref = alias {x => $x, y => $y};
Note that this also works:
alias my $arrayref = [$x, $y, $z];
alias my $hashref = {x => $x, y => $y};
but this makes the lhs an alias to the temporary, and therefore read-only,
reference made by "[]" or "{}". Therefore later
attempts to assign to $arrayref or $hashref results in an error. The
anonymous aggregate that is referenced behaves the same in both cases
obviously.
- Array insertions
- These work as usual, except the inserted elements are
aliases.
alias push @array, $foo;
alias unshift @array, $foo;
alias splice @array, 1, 2, $foo;
An attempt to do any of these on tied (or "magical") array will
result in a "Can't push/unshift/splice alias onto tied array"
error.
- Returning an alias
- Returns aliases from the current "sub" or
"eval". Normally this only happens for lvalue subs, but
"alias return" can be used in any sub. Lvalue subs only work for
scalar return values, but "alias return" can handle a list of
return values.
A sub call will very often copy the return value(s) immediately after they
have been returned. "alias return" can't prevent that. To pass
an alias through a sub return and into something else, the call site must
process the return value using an aliasing operation, or at least a
non-copying one. For example, ordinary assignment with the sub call on the
right hand side will copy, but if the call site is in the scope of an
"alias" pragma then the assignment will instead alias the return
value.
When alias-returning a list of values from a subroutine, each individual
value in the list is aliased. The list as a whole is not aliasable; it is
not an array. At the call site, a list of aliases can be captured into
separate variables or into an array, by an aliasing list assignment.
- Subroutines and evaluations
- Placing a subroutine or "eval STRING" inside
"alias" causes it to be compiled with aliasing semantics
entirely. Additionally, the return from such a sub or eval, whether
explicit using "return" or implicitly the last statement, will
be an alias rather than a copy.
alias { sub foo { $x } };
my $subref = alias sub { $x };
my $xref1 = \foo;
my $xref2 = \alias eval '$x';
my $xref3 = \$subref->();
Explicitly returning an alias can also be done using "alias
return" inside any subroutine or evaluation.
sub foo { alias return $x; }
my $xref = \foo;
- Localization
- Use of local inside "alias" usually behaves the
same as local does in general, however there is a difference if the
variable is tied: in this case, Perl doesn't localize the variable at all
but instead preserves the tie by saving a copy of the current value, and
restoring this value at end of scope.
alias local $_ = $string;
The aliasing semantics of "local" avoids copying by always
localizing the variable itself, regardless of whether it is tied.
IMPLEMENTATION¶
This module does
not use a source filter, and is therefore safe to use
within eval STRING. Instead, Data::Alias hooks into the Perl parser, and
replaces operations within the scope of "alias" by aliasing
variants.
For those familiar with perl's internals: it triggers on a ck_rv2cv which
resolves to the imported "alias" sub, and does a parser hack to
allow the "alias BLOCK" syntax. When the ck_entersub is triggered
that corresponds to it, the op is marked to be found later. The actual work is
done in a peep-hook, which processes the marked entersub and its children,
replacing the pp_addrs with aliasing replacements. The peep hook will also
take care of any subs defined within the lexical (but not dynamical) scope
between the ck_rv2cv and the ck_entersub.
KNOWN ISSUES¶
- Lexical variables
- When aliasing existing lexical variables, the effect is
limited in scope to the current subroutine and any closures create after
the aliasing is done, even if the variable itself has wider scope. While
partial fixes are possible, it cannot be fixed in any reliable or
consistent way, and therefore I'm keeping the current behaviour.
When aliasing a lexical that was declared outside the current subroutine, a
compile-time warning is generated "Aliasing of outer lexical variable
has limited scope" (warnings category "closure").
ACKNOWLEDGEMENTS¶
Specials thanks go to Elizabeth Mattijsen, Juerd Waalboer, and other members of
the Amsterdam Perl Mongers, for their valuable feedback.
AUTHOR¶
Matthijs van Duin <xmath@cpan.org> developed the module originally, and
maintained it until 2007. Andrew Main (Zefram) <zefram@fysh.org> updated
it to work with Perl versions 5.11.0 and later.
LICENSE¶
Copyright (C) 2003-2007 Matthijs van Duin. Copyright (C) 2010, 2011 Andrew Main
(Zefram) <zefram@fysh.org>. All rights reserved. This program is free
software; you can redistribute it and/or modify it under the same terms as
Perl itself.