Perl's subroutines accept parameters as flat lists of scalars. An array passed as a parameter is for all practical purposes a flat list too. Even a hash is treated as a flat list of one key followed by one value, followed by one key, etc.
A flat list is not passed as a reference unless you do so explicitly. The fact that modifying $_[0]
modifies $a[0]
is because the elements of @_
become aliases for the elements passed as parameters. Modifying $_[0]
is the same as modifying $a[0]
in your example. But while this is approximately similar to the common notion of "pass by reference" as it applies to any programming language, this isn't specifically passing a Perl reference; Perl's references are different (and indeed "reference" is an overloaded term). An alias (in Perl) is a synonym for something, where as a reference is similar to a pointer to something.
As perlsyn states, if you assign to @_
as a whole, you break its alias status. Also note, if you try to modify $_[0]
, and $_[0]
happens to be a literal instead of a variable, you'll get an error. On the other hand, modifying $_[0]
does modify the caller's value if it is modifiable. So in example one, changing $_[0]
and $_[1]
propagates back to @a
because each element of @_
is an alias for each element in @a
.
Your second example is a little tricky. Hash keys are immutable. Perl doesn't provide a way to modify a hash key, aside from deleting it. That means that $_[0]
is not modifiable. When you attempt to modify $_[0]
Perl cannot comply with that request. It probably ought to throw a warning, but doesn't. You see, the flat list passed to it consists of unmodifiable-key followed by modifiable-value, etc. This is mostly a non-issue. I cannot think of any reason to modify individual elements of a hash in the way you're demonstrating; since hashes have no particular order you wouldn't have simple control over which elements in @_
propagate back to which values in %a
.
As you pointed out, the proper protocol is to pass \@a
or \%a
, so that they can be referred to as $_[0]->{element}
or $_[0]->[0]
. Even though the notation is a little more complicated, it becomes second nature after awhile, and is much clearer (in my opinion) as to what is going on.
Be sure to have a look at the perlsub documentation. In particular:
Any arguments passed in show up in the array @_
. Therefore, if you called a function with two arguments, those would be stored in $_[0]
and $_[1]
. The array @_
is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0]
is updated, the corresponding argument is updated (or an error occurs if it is not updatable). If an argument is an array or hash element which did not exist when the function was called, that element is created only when (and if) it is modified or a reference to it is taken. (Some earlier versions of Perl created the element whether or not the element was assigned to.) Assigning to the whole array @_
removes that aliasing, and does not update any arguments.