I was always sure that if I pass a Perl subroutine a simple scalar, it can never change its value outside the subroutine. That is:
my $x = 100;
foo($x);
# without knowing anything about foo(), I'm sure $x still == 100
So if I want foo() to change x, I must pass it a reference to x.
Then I found out this is not the case:
sub foo {
$_[0] = 'CHANGED!';
}
my $x = 100;
foo($x);
print $x, "\n"; # prints 'CHANGED!'
And the same goes for array elements:
my @arr = (1,2,3);
print $arr[0], "\n"; # prints '1'
foo($arr[0]);
print $arr[0], "\n"; # prints 'CHANGED!'
That kinda surprised me. How does this work? Isn’t the subroutine only gets the value of the argument? How does it know its address?
In Perl, the subroutine arguments stored in
@_are always aliases to the values at the call site. This aliasing only persists in@_, if you copy values out, that’s what you get, values.so in this sub:
Note that this aliasing happens after list expansion, so if you passed an array to
example, the array expands in list context, and@_is set to aliases of each element of the array (but the array itself is not available toexample). If you wanted the latter, you would pass a reference to the array.Aliasing of subroutine arguments is a very useful feature, but must be used with care. To prevent unintended modification of external variables, in Perl 6 you must specify that you want writable aliased arguments with
is rw.One of the lesser known but useful tricks is to use this aliasing feature to create array refs of aliases
or aliased slices:
it also turns out that using
sub {\@_}->(LIST)to create an array from a list is actually faster than[ LIST ]since Perl does not need to copy every value. Of course the downside (or upside depending on your perspective) is that the values remain aliased, so you can’t change them without changing the originals.As tchrist mentions in a comment to another answer, when you use any of Perl’s aliasing constructs on
@_, the$_that they provide you is also an alias to the original subroutine arguments. Such as:Lastly all of this behavior is nestable, so when using
@_(or a slice of it) in the argument list of another subroutine, it also gets aliases to the first subroutine’s arguments: