Re: [Boston.pm] something about @_ is different?
Ben Tilly via Boston-pm <[email protected]> Wed, 22 May 2019 16:00:26 -0700
| Newsgroups | gmane.comp.lang.perl.perl-mongers.boston |
|---|---|
| Message-ID | <CANoac9VPKMMRy2h7SdDjFcU8PFSpMQ+fxHCkkpfZE8XQO8QezA@mail.gmail.com> |
Your three rules are wrong. More specifically, rule 1 is wrong, which is
why you immediately needed rule 2 to correct that mistake, and is also why
rule 3 came as a surprise. Drawing an analogy to references is misleading
at best, and it is no wonder that your expectations went wrong from there.
There are three things that are special about @_.
1. Its initial contents are _aliases_ to the variables that were passed in.
2. It is dynamically scoped.
3. @_ is always @main::_, no matter what package you are in.
That's it. To understand the rest, think about what is happening under the
hood. An array is a list of scalar values. A scalar value points at
data. A reference is when that data points at some other Perl data
structure, which could be a scalar value, or whatever else. An alias is
not that, it is just another way to get at the same scalar value. (Note,
Perl uses aliases in various ways. 5.22 exposes aliases to users in a more
obvious way. They are not the same thing as references.)
Now for *any* array, if you assign to the array you throw away its
attachment to all existing scalars, create new ones, and copy the assigned
data by value. That's how assignment by value works. Do that to @_ and
you throw away all of the aliases you have, create new scalars, and copy
data to them. Therefore the magic connection between @_ and what was
passed in gets lost. This should be no more mysterious than the fact that
when you push onto @_ you now have some aliased variables in @_, and some
that are local to your function.
You can prove this to yourself with the following code sample:
sub demo {
for my $thing (@_) {
print "Start " . \$thing . "\n";
}
@_ = @_;
for my $thing (@_) {
print "End " . \$thing . "\n";
}
}
my @list = 1..5;
for my $thing (@list) {
print "Before call " . \$thing . "\n";
}
demo(@list);
for my $thing (@list) {
print "After call " . \$thing . "\n";
}
What you will see is the scalars in @list both before and after the call
match what is in @_ before you assign to it. But after assigning @_ to
itself inside the function, @_ has all new variables. If you added prints
of \@_ and \@list, you'd find that @_ is the same array both before and
after the assignment, and it is a different array than @list.
On Wed, May 22, 2019 at 3:09 PM Greg London <email-2Ro/Dj86MvDSUeElwK9/[email protected]> wrote:
>
> On Tue, May 21, 2019 7:18 pm, Ben Tilly wrote:
> > That said, it is actually consistent.
>
> my $alpha="aaa";
> my $bravo="bbb";
>
> my @arr_o_refs = ( \$alpha, \$bravo );
>
> @arr_o_refs = reverse(@arr_o_refs);
>
> print Dumper \@arr_o_refs;
>
> [
> \"bbb",
> \"aaa"
> ]
>
> If someone knew the rules to how normal arrays worked in perl,
> they need to learn 3 different exceptions to understand @_ such as:
>
> 1: @_ is like an array of references back to the original caller variables.
>
> 2: but when you get/set an element in @_, perl automatically dereferences
> the reference for you.
>
> 3: assigning to the entire @_ array doesnt act like assigning to a
> normal array of references. Rather its like calling local() on the
> @_ array, but then it also ignores lexical scope normally associated
> with local()
>
>
> "It all follows logically."
>
>
> Its logically consistent to :
> 1: the rules of arrays
> PLUS
> 2: several exceptions to the rules of arrays that only applies to @_
>
> To that set of rules, with the exceptions, it is logical.
>
>
>
>
> On Tue, May 21, 2019 7:18 pm, Ben Tilly wrote:
> > If you want to assign to all the individual elements in the passed in
> > array, you can. Try this:
> >
> > sub swapper { @_[0..$#_] = reverse @_;
> > }
> >
> >
> > That said, it is actually consistent. It just doesn't match the mental
> > model that you had.
> >
> > Perl is pass by reference, assign by value. All else follows.
> >
> >
> > @_ starts off as an array of aliased elements. But assign to @_, and
> > you've created a new array with new values. Assign to the slots of @_,
> > and you've replaced the old values with new values in the original
> > variables. (That's the "pass by reference" bit.) If you assign one value
> > to another, you copy the value. If you then modify the second copy in
> > place, the original does not change.
> >
> > The assignment by value is a shallow copy. That means that when you copy
> > a reference, both copies point to the same underlying data. Compare the
> > following:
> >
> >
> > my $foo = "Hello"; my $bar = $foo; # Copies data $bar .= ", World";
> > say($foo); # Prints "Hello\n"
> >
> > my $foo = \"Hello"; my $bar = $foo; # Copies reference to same data $$bar
> > .= ", World";
> > say($$foo); # Prints "Hello, World\n"
> >
> > It all follows logically. And, modulo various optimizations, this is
> > exactly how it works under the hood.
> >
> > On Tue, May 21, 2019 at 3:50 PM Greg London <email-2Ro/Dj86MvDSUeElwK9/[email protected]>
> wrote:
> >
> >
> >> Ugh. I dont like this.
> >>
> >>
> >> Its linguistically inconsistent.
> >> Assign to an element in array assigns back to caller var.
> >> Assign to whole array disconnects the aliases
> >> that ties @_ back to the caller vars and assigns to a local version of
> >> @_?
> >>
> >>
> >> If someone wants to disconnect the underlying aliases,
> >> call local() and put the syntactic sugar there.
> >>
> >> And then assigning to @_ without local() would work just
> >> like assigning to all the individual elements in array.
> >>
> >> Perl can be disappointing sometimes.
> >>
> >>
> >> Greg
> >>
> >>
> >>
> >>
> >> On Tue, May 21, 2019 2:12 pm, Conor Walsh wrote:
> >>
> >>> Assigning to @_ is weird and I don't recommend it. It's full of magic
> >>> aliases, which in some cases means you change the caller's
> >>> variables, which is usually not what you want.
> >>>
> >>> Check the beginning of perldoc perlsub.
> >>>
> >>>
> >>>
> >>> On Tue, May 21, 2019, 2:04 PM Greg London <email-2Ro/Dj86MvDSUeElwK9/[email protected]>
> >>> wrote:
> >>>
> >>>
> >>>
> >>>>
> >>>> Hm, it could be a caffeine defficiency, but it seems that @_ is
> >>>> being treated different than other arrays.
> >>>>
> >>>> I can create a normal array:
> >>>> my @normal_array=('a','b');
> >>>>
> >>>> and I can assign an entirely new list to it:
> >>>> @normal_array=reverse(@normal_array); # assign to array
> >>>>
> >>>>
> >>>>
> >>>> and I can assign to it in parenthesis an entirely new list
> >>>> (@normal_array)=reverse(@normal_array); # assign to (array)
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> And those assignements take hold as I would expect.
> >>>>
> >>>>
> >>>>
> >>>> When I use the exact same syntax for the @_ array instead
> >>>> of @normal_array, it doesn't work as expected: sub swapper{ (@_) =
> >>>> reverse(@_); # this doesn't seem to do anything. }
> >>>>
> >>>>
> >>>> my @other=('x','y'); swapper(@other); print Dumper \@other;
> >>>>
> >>>> Pretty sure this code used to work,
> >>>> but that was many moons ago.
> >>>>
> >>>> Thoughts?
> >>>>
> >>>>
> >>>>
> >>>> Greg
> >>>>
> >>>>
> >>>>
> >>>> Here's the script in one big copy/paste block:
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> my @normal_array=('a','b'); print "normal_array is "; print Dumper
> >>>> \@normal_array;
> >>>> @normal_array=reverse(@normal_array); # assign to array
> >>>> print "normal_array is "; print Dumper \@normal_array;
> >>>> (@normal_array)=reverse(@normal_array); # assign to (array)
> >>>> print "normal_array is "; print Dumper \@normal_array;
> >>>>
> >>>>
> >>>> sub swapper{ (@_) = reverse(@_); }
> >>>>
> >>>>
> >>>>
> >>>> my $one = "i am one"; my $two = "i am two";
> >>>>
> >>>> swapper($one,$two);
> >>>>
> >>>> warn "one is '$one'"; warn "two is '$two'";
> >>>>
> >>>> _______________________________________________
> >>>> Boston-pm mailing list
> >>>> [email protected]
> >>>> https://mail.pm.org/mailman/listinfo/boston-pm
> >>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >> --
> >>
> >>
> >> _______________________________________________
> >> Boston-pm mailing list
> >> [email protected]
> >> https://mail.pm.org/mailman/listinfo/boston-pm
> >>
> >>
> >
>
>
> --
>
>
>