$a and $b clearly are special - they are allowed to violate strict 'vars', due to their role as placeholders in the optional sort comparison function. This is the case even when no sort is in sight.

Turning our attention to sort, I have had need recently for externalising the compare function. Consider this:

my $obj = Foo->new( sort => 1); ... while (my $val = $obj->next) {
I have a new object of class Foo, which has an iterator. In my design of Foo.pm, I want the user to be able to choose to sort or not to sort via the 'sort' parameter. What I want next is to allow the user instead, to supply a CODEREF for a compare routine, to allow custom sort orders.

Conventially, the way a sort compare routine receives its operands is via $a and $b, and this is what I first envisaged. But, what do these actually mean? If called from package main, these will be $main::a and $main::b - fine, but my code is looking at package Foo by default.

I could look at caller in my new routine, and export $a and $b into the caller's package name space, or import the caller's $a and $b, or hold globrefs so that I know which $a and $b are being referred to. This is messy.

I have had a discussion with a respected module author about this subject, and he sympathised with me about my plight regarding $a and $b.

I have thought up an alternative, which is for the user to supply a routine which expects the operands in @_ instead. These are nicely localised by the parameter passing mechanism, so as not to interfere with other outer sorts that may be happening. In order to make my code work, I pass in $a and $b into the called routine using an anonymous wrapper.

if ($self->{sort}) { @out = (ref($self->{sort}) eq 'CODE') ? (sort {&{$self->{sort}}($a,$b)} @out) : (sort @out); }
I clearly state in the POD that the compare routine will be passed its operands in @_. Is there anything wrong with this approach from a user perspective? If not, why should we not look to dispense with $a and $b altogether in a future perl release? I know $a and $b will be gone in perl 6, but I think that this anachronism ought to be gone sooner than that.

--
I'm Not Just Another Perl Hacker

Replies are listed 'Best First'.
Re: Why do we need $a and $b to be special?
by ysth (Canon) on Jul 30, 2004 at 02:21 UTC
    By default, $a and $b will be with us forever (in perl5) for backward compatibility; however, since 5.6.0, perl has supported having a named comparison routine with a $$ prototype to indicate that you want the elements to sort passed as parameters instead of as $a and $b:
    $ perl -we'sub evenfirst ($$) { ($_[0]&1) <=> ($_[1]&1) or $_[0] <=> $ +_[1] } print sort evenfirst 0..9' 0246813579
      ... however, since 5.6.0, perl has supported having a named comparison routine with a $$ prototype to indicate that you want the elements to sort passed as parameters instead of as $a and $b
      I didn't know that. How would the $$ prototype be seen in a CODE ref? Would it still work?

      I tend to avoid prototypes normally, as they don't do what you expect, and seem to be more trouble than they are worth.

      --
      I'm Not Just Another Perl Hacker

        I didn't know that. How would the $$ prototype be seen in a CODE ref? Would it still work?

        Just apply the prototype to the coderef, for example:

        my $sort = sub ($$) { $_[0] cmp $_[1] }; my @sorted = sort $sort @unsorted;
        Agree with you re: prototypes. I don't think you can do it with a code block specified after "sort", you actually have to declare a sub. (However, in general you can attach prototypes to coderefs by putting the prototype between "sub" and the code block.)

        I think this was implemented because without it you can't very well use a sort subroutine from another package, since it will be looking at that package's $a and $b, while sort will be setting the current package's $a and $b.

Re: Why do we need $a and $b to be special?
by Zaxo (Archbishop) on Jul 30, 2004 at 00:48 UTC

    Not relevant to the usefulness or not of $a and $b, but why not have some sort of class factory which produces a package scoped Foo::* class for each comparison function you provide?

    Another entertaining choice would be to overload <=> and cmp and let sort sort it out. I've never tried that, but it sounds like fun.

    After Compline,
    Zaxo

Re: Why do we need $a and $b to be special?
by itub (Priest) on Jul 30, 2004 at 01:00 UTC
    I don't know what other people think, but I did exactly what you suggest somewhere in my Chemistry::Mol module. From the POD:
    $mol->sort_atoms($sub_ref)

    Sort the atoms in the molecule by using the comparison function given in $sub_ref. This function should take two atoms as parameters and return -1, 0, or 1 depending on whether the first atom should go first, same, or after the second atom. For example, to sort by atomic number, you could use the following:

    $mol->sort( sub { $_[0]->Z <=> $_[1]->Z } );

    Note that the atoms are passed as parameters and not as the package variables $a and $b like the core sort function does. This is because $mol->sort will likely be called from another package and we don't want to play with another package's symbol table.

Re: Why do we need $a and $b to be special?
by theorbtwo (Prior) on Jul 30, 2004 at 09:25 UTC

    IIRC, the original reason for $a and $b is that they were much faster then using @_. That's less true then it used to be, but it's still the case that most sort routines are trivial enough that writing $_[0] and $_[1] instead of $a and $b makes them significantly more complex-looking.

    In any case, as you say, they'll be gone in perl6. They aren't going away in perl5, because it'd instantly break everybody's code.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      IIRC, the original reason for $a and $b is that they were much faster then using @_.

      I thought the original reason was that sort with a block was in perl4 and there were no subroutines at that time, but I might be wrong.

        Even Perl 1 had subroutines. I don't have time to look at how sort worked then, but I do remember having to say somethign like do &subname to make subroutine calls in Perl 1.

        Of course, now that I post, I immediately realize you probably meant didn't have lexicals, which is true.

Re: Why do we need $a and $b to be special?
by dakkar (Hermit) on Jul 30, 2004 at 12:56 UTC

    In a module of mine (might event release it at some time), I needed the same functionality, so I wrote:

    my $caller=(caller(1))[0]; no strict 'refs'; @out=sort { local (${"${caller}::a"},${"${caller}::b"})=($a,$b); $sort->($a,$b) } @in;

    This might not give the fastest runtimes, but allows the user to pass a compare sub that works in any of the two ways.

    -- 
            dakkar - Mobilis in mobile
    

    Most of my code is tested...

    Perl is strongly typed, it just has very few types (Dan)

Re: Why do we need $a and $b to be special?
by gmpassos (Priest) on Jul 30, 2004 at 20:41 UTC
    Sort data is not a fast thing to do. Generally we are sorting just a small list when using the command sort of Perl, but even for this small amout sort need to be a fast thing. So, we use a speciall variable that will point to each element in the list (array), avoiding the copy of the strings/scalars to compare them. @_ also does the same thing, but access directly a scalar ($a) is much more faster than access an element of an array (@_[0]).

    Graciliano M. P.
    "Creativity is the expression of the liberty".