Skip Menu |

Preferred bug tracker

Please visit the preferred bug tracker to report your issue.

This queue is for tickets about the Perl-Critic CPAN distribution.

Report information
The Basics
Id: 74429
Status: resolved
Priority: 0/
Queue: Perl-Critic

People
Owner: Nobody in particular
Requestors: EDAVIS [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 1.116
Fixed in: (no value)



Subject: 'Negative array index should be used' a little misleading
Variables::RequireNegativeIndices complains about $x[$#x-1] and suggests using a negative array index instead. But in fact $x[-2] does not have quite the same semantics. If the array has only a single element then $x[$#x-1] will give that element, but $x[-2] is undef. Similarly $x[@x-2] is *not* equivalent to $x[-2], as the long text for this policy claims. It is still true that using ($#x - something) in an array index is usually bad practice, and that some kind of expression using a negative index would be a better way to write it. But the current short and long messages give the impression that you can simply replace the troublesome expression with one using a negative offset. As a special case $x[$#x] and $x[@x-1] and $x[-1] are indeed equivalent. The policy can suggest changing those without caveats. If the array index is not one of these cases then it needs to give a more nuanced warning.
On Thu Jan 26 11:48:14 2012, EDAVIS wrote: Show quoted text
> Variables::RequireNegativeIndices complains about $x[$#x-1] and > suggests using a negative array index instead. But in fact $x[-2] > does not have quite the same semantics. If the array has only a > single element then $x[$#x-1] will give that element, but $x[-2] > is undef. > > Similarly $x[@x-2] is *not* equivalent to $x[-2], as the long text > for this policy claims. > > It is still true that using ($#x - something) in an array index is > usually bad practice, and that some kind of expression using a > negative index would be a better way to write it. But the current > short and long messages give the impression that you can simply > replace the troublesome expression with one using a negative offset. > > As a special case $x[$#x] and $x[@x-1] and $x[-1] are indeed > equivalent. The policy can suggest changing those without caveats. > If the array index is not one of these cases then it needs to give > a more nuanced warning.
Okay, I'm not Conway or any of the other Perl demiurges. But to my mind the real reason that things like $x[@x-2] should not be used is that the semantics change based on whether the computed index is negative, and if I were rewriting the long explanation that's what I would focus on, rather than the special cases where the three different constructions actually give the same thing all the time. Something like =pod Perl treats a negative array subscript as an offset from the end. Given this, the preferred way to get the last element is C<$x[-1]>, not C<$x[$#x]> or C<$x[@x-1]>, and the preferred way to get the next-to-last is C<$x[-2]>, not C<$x[$#x-1> or C<$x[@x-2]>. The biggest argument against the non-preferred forms is that their semantics change when the computed index becomes negative. If C<@x> contains at least two elements, C<$x[$#x-1]> and C<$x[@x-2]> are equivalent to $x[-2]. But if it contains a single element, C<$x[$#x-1]> and C<$x[@x-2]> are equivalent to $x[-1]. Simply put, the preferred form is more likely to do what you actually want. As Conway points out, the preferred forms also perform better, are more readable, and are easier to maintain. This policy notices all of the simple forms of the above problem, but does not recognize any of these more complex examples: $some->[$data_structure]->[$#{$some->[$data_structure]} -1]; my $ref = \@arr; $ref->[$#arr]; =cut As for the short message, it's hard to be very nuanced in just one line. You say that the short message gives the impression that the different forms are equivalent, but it seems to me that "Negative array index should be used" is silent on whether they are exactly equivalent.
The long text you suggest looks good. For the one-liner, how about Consider using negative array index instead
On Mon Jan 30 07:05:37 2012, EDAVIS wrote: Show quoted text
> The long text you suggest looks good. For the one-liner, how about > > Consider using negative array index instead
"Consider" is a fairly weak word. It seems to me that someone who is not serious about implementing the recommendation of a given policy should not run that policy. Perl::Critic is, after all, a tool, not a straitjacket. Given that: * This policy is being run, * This policy's point is to promote the use of negative indices, * This policy's name is "RequireNegativeIndices" (not 'Suggest...), * This policy's severity is "high", I feel that "Negative array index should be used" is a reasonable statement of the purpose of the policy. Do you have a real-world example that needs to be implemented by backing off from the array size or last index rather than using a negative index? I have come up with a couple examples, but they seem to me to be very contrived, and to produce monumentally unclear code. 1) If you iterate from -@array to $#array you iterate over the array twice. But if you are going to iterate more than once, what stops you from doing it three times, or more? Besides, this policy does not actually prevent 'for $inx ( -@array .. $#array ) { ... }', which is the natural way to do this. 2) If you are sufficiently "clever", you might be be able to get a default applied somehow, but I have not been able to contrive an actual example. It seems to me that the more maintainable way to do this would be to apply the default explicitly, because that way you don't have to worry about the index becoming -2 somehow.
OK, I'm not that exercised about the short text for this policy. (The long text as it currently stands, however, is factually incorrect and should be changed.) The real-world example that motivated this bug report was that when maintaining an older program, prompted by this policy, I globally changed $x[$#x] to $x[-1] and $x[$#x-1] to $x[-2]. That latter change is unsound. So the real-world example is an example of human fallibility rather than some code which particularly enjoys the semantics of $x[$#x-1].
On Thu Feb 09 11:52:16 2012, EDAVIS wrote: Show quoted text
> OK, I'm not that exercised about the short text for this policy. > (The long text as it currently stands, however, is factually incorrect > and should be changed.) > > The real-world example that motivated this bug report was that when > maintaining an older program, prompted by this policy, I globally > changed $x[$#x] to $x[-1] and $x[$#x-1] to $x[-2]. That latter change > is unsound. > So the real-world example is an example of human fallibility rather than > some code which particularly enjoys the semantics of $x[$#x-1]. >
So someone wrote code that depended on the semantics of $x[$#x-1], and you got burned straightening it out? Yukkk! Jeff and Elliot - are you on board with making some change like the POD proposed up-thread?
Subject: Re: [rt.cpan.org #74429] 'Negative array index should be used' a little misleading
Date: Fri, 24 Feb 2012 22:59:05 -0800
To: bug-Perl-Critic [...] rt.cpan.org
From: Jeffrey Thalhammer <jeff [...] imaginative-software.com>
On Feb 24, 2012, at 10:26 PM, Tom Wyant via RT wrote: Show quoted text
> Jeff and Elliot - are you on board with making some change like the POD > proposed up-thread?
It hurts my head just thinking about these expressions. Ed has clearly thought about this a lot. So I vote to do whatever he says. -Jeff
Change to long description of policy committed as SVN revision 4120. This change is approximately what was proposed above, with a word here and there tweaked, and the phrase about the semantic change made bold.
On Mon Feb 27 21:54:46 2012, WYANT wrote: Show quoted text
> Change to long description of policy committed as SVN revision 4120. > This change is approximately what was proposed above, with a word here > and there tweaked, and the phrase about the semantic change made bold.
PS - I reverted the MANIFEST change before committing this, in an attempt _not_ to further entangle the whole MANIFEST issue.
Thanks for working on this.
Argh - I asked the rt web interface to keep the status at 'patched' but it decided to reopen the bug when adding my comment.
Fixed and released in Perl-Critic-1.118. Thanks for reporting this. -- Jeffrey Thalhammer Imaginative Software Systems www.imaginative-software.com