A Subtlety of 'reverse'

ferreira on 2008-02-07T18:08:47

A few days ago I was bitten by the dual nature of reverse.

$s = reverse 'xyz'; # 'zyx'
@a = reverse qw(a b c); # qw(c b a)

This is even worse because reverse in scalar context may receive a LIST as argument and concatenates the reverse string of each element.

$s = reverse qw(abc mni xyz); # 'zyxinmcba'

There was a code where a method history was meant to return an array and got used in a few places this way:

$history_size = $o->history();

which worked ok, until I found a subclass implementation which had been coded like this:

sub history {
    shift;
    return ( reverse @history );
}

and the assumption that it should return the (history) array size when called in scalar context failed miserably. The solution was to document what it was supposed to return in list and scalar contexts and to change the implementation to:

sub history {
    shift;
    return my @h = reverse @history;
}

That's the kind of thing I remember when someone says: "What's so wrong with Perl 5 that we might need Perl 6?"

That's the kind of subtlety that caught you by surprise occasionally, beginner or not. Idiosyncracies like that put a burden at the programmer which has not a perfect memory (and I am one of such programmers unfortunately, I have to admit) and makes some feel that a Perl guru is needed to detect, solve and explain such bits. This kind of weird behavior leads sometimes to workarounds because there is no time to think about what's wrong with that f... piece of code. And then it distorts into ugly things that contributes to the bad fame of our favourite language.

Such ugly things go like a current joke on Portuguese ortography. A friend tells the other: "Write me a check of 'sessenta' Brazilian Reais" -- "How dow I spell 'sessenta'?" -- "Well, make two checks of thirty." (Background: the word "sessenta" (sixty) may raise doubts to careless people about the right spelling: "sesenta", "seçenta", "sessenta".)

If Perl 6 fails to get rid of such subtleties (and this is one of its design principles), it will have failed to be the improvement of the Perl language it was supposed to be.


on a sidenote..

Qiang on 2008-02-07T18:45:58

i would name the function name as history_size instead of history to avoid confusion. but i agree what you have said.

Re:on a sidenote..

ferreira on 2008-02-07T19:17:56

Most of the time, the method is used to get the whole history as an array.

To give a more concrete background, I was working with the GetHistory method from Term::ReadLine::Gnu, Term::ReadLine::Perl and Term::ReadLine::Zoid when used by Shell::Base. That said, instead of trying to convince all involved authors to change code or/and documentation, I tried to work with the contrived and actual API.

I agree that introducing a GetHistorySize method would eliminate instantly the problem associated to the overloaded meaning of GetHistory at list and scalar contexts.

ugh! use "wantarray"!

merlyn on 2008-02-07T19:18:06

sub history { shift; return my @h = reverse @history; }

Ugh! You really have two return values, so say so: sub history { shift; return wantarray ? reverse @history : scalar @history; } This saves the extra work of reversing something that you don't need to reverse.

sort too

jplindstrom on 2008-02-07T22:44:39

This goes for "sort" too.

I'd say that in application code 97.21% (plus/minus .03%) of all subs are meant to be context insensitive.

So there really is a need for a Perl::Critic plugin that warns against "return CALL_TO_CONTEXT_AWARE_FUNCTION" where the context of the caller leaks into the sub and causes this severe action-at-a-distance.

(meta: five timeouts posting this)