Am I missing some semantics of $_ here?
perl
Solution
There is a subtle edge case here: Inside `foreach` loops, or `map` expressions, the default variable `$_` is aliased to the original value. E.g.
@nums = 1..5;
@foo = map { $_ *= 2 } @nums;
# both @foo and @nums contain 2, 4, 6, 8, 10 now.
However, constants aren't valid lvalues, so we couldn't do that like
@foo = map { $_ *= 2 } 1, 2, 3, 4, 5;
# Modification of read-only value
The `@_` array too is aliased to the original values, so imagine the following edge cases:
sub buggy (&@) { my $cb = shift; map $cb->($_), @_ };
buggy { $_ *= 2 } 1, 2, 3; # Modification of read-only value attempted
buggy { $_[0] *= 2} 1, 2, 3; # ditto
my @array = 1 .. 5;
buggy { $_ *= 2 } @array; # @array now is 2, 4, 6, 8, 10
buggy { $_[0] *= 2 } @array; # ditto
Aliases are transitive, so inner `$_[0]` is aliased to `$_`, which is aliased to outer `$_[0]`, which is an alias for the constant `1` / `$array[0]`.
So, what does `local $_ = $_[$_]` do here?
- It makes a copy of the value, thus avoiding this insane aliasing behaviour
- It shows the intent to make `$_` visible to the callback.
Ensuring copying semantics (thus avoiding unexpected side effects) feels natural for Perl, so this function is well-designed and not especially overengineered.
(Note: `map {local $_ = $_; ...} @_` would have been sufficient to make the copy)
Problem
During profiling, I came across this function in List::UtilsBy: ``` sub rev_nsort_by(&@) { my $keygen = shift; my @keys = map { local $_ = $_[$_]; scalar $keygen->( $_ ) } 0 .. $#_; return map { $_[$_] } sort { $keys[$b] <=> $keys[$a] } 0 .. $#_; } ``` rev_nsort_by does a reverse numeric sort based on some key predicate, for example: ``` my @objects = load_objects_from_database(); # sort by rating, highest first @objects = rev_nsort_by { $_->rating } @objects; ``` I understand perfectly why `rev_nsort_by`, as shown above, works as intended, but I'm wondering why it's so complex. Specifically, I wonder why ``` my @keys = map { local $_ = $_[$_]; scalar $keygen->( $_ ) } 0 .. $#_; ``` was not written as ``` my @keys = map { scalar $keygen->( $_ ) } @_; ``` which looks functionally equivalent to me. Am I missing some corner-case behavior of `$_` here, which the longer version amounts for in some way?