NAME
Foreign::Sort - subroutine attribute to allow call to sort routine from other package
VERSION
0.01
SYNOPSIS
package X1;
use Foreign::Sort;
sub by_middle : Foreign {
(substr($a,4) // "") cmp (substr($b,4) // "")
|| $a cmp $b
}
package X2;
@env_keys = sort X1::by_middle keys %ENV;
THE PROBLEM
The builtin "sort" function takes an optional subroutine name to use as a comparison function. Just before calling the comparison function, Perl temporarily sets the variables $a
and $b
from the calling package with the values to be compared. The comparison function is expected to decide an ordering for $a
and $b
and to return an appropriate value.
A problem arises when the calling package is not the same as the package that defines the comparison function.
package X2;
sort by_42 {
($b eq '42') <=> ($a eq '42) || $a <=> $b
}
@y = (17, 19, 42, 83, 47);
@yy = sort X2::by_42 @y;
package X1;
@x = (17, 19, 42, 83, 47);
@xx = sort X2::by_42 @x;
The first sort
call will succeed (returning the values in the order 42,17,18,47,83
) but the second sort
call will fail. This is because the by_42
function, declared in package X2
, is implictly operating on the package variables $X2::a
and $X2::b
, and the sort call from package X1
is setting the package variables $X1::a
and $X1::b
instead.
One relatively common place this problem arises is in inheritance heirarchies, where it may be cumbersome to use a comparison function in a superclass from a subclass.
THE SOLUTION
The Foreign::Sort
package defines the subroutine attribute Foreign
that can be applied to comparison functions. A comparison function with the Foreign
attribution will perform its comparison on the $a
and $b
values from the calling package, not (necessarily) the package where the comparison function is defined. This allows you to define a comparison function that other users may call from other packages, and save them the trouble of setting $a
and $b
in the right package.
package X2;
use Foreign::Sort;
sub by_42 : Foreign {
($b eq '42') <=> ($a eq '42) || $a <=> $b
}
package X1;
@x = (17, 19, 42, 83, 47);
@xx = sort X2::by_42 @x;
In this case, the call succeeds because the Foreign::Sort
package was copying the values from $X1::a
and $X1::b
to $X2::a
and $X2::b
with each call to the X2::by_42
function.
This module was inspired by a discussion at https://stackoverflow.com/q/54842607.
LIMITATIONS
All testing for initial release done on Perl v5.22 and better. Future versions will attempt to make this module compatible with older Perls, if necessary.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Foreign::Sort
You can also look for information at:
RT: CPAN's request tracker (report bugs here)
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
LICENSE AND COPYRIGHT
Copyright (c) 2019, Marty O'Brien.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.1 or, at your option, any later version of Perl 5 you may have available.
See http://dev.perl.org/licenses/ for more information.