According to the documentation, sort compares using infix:<cmp>.
But:
class Point
{
has Int $.x;
has Int $.y;
method Str { "($!x,$!y)" }
method gist { self.Str }
}
multi sub infix:<cmp>(Point $a, Point $b) { $a.y cmp $b.y || $a.x cmp $b.x }
my @p = Point.new(:3x, :2y), Point.new(:2x, :4y), Point.new(:1x, :1y);
say @p.sort;
gives output:
((1,1) (2,4) (3,2))
When I use:
say @p.sort(&infix:<cmp>);
it does give the proper output:
((1,1) (3,2) (2,4))
Is this a bug, a feature, or a flaw in the documentation?
And is there a way to make .sort() on a list of a Points use a custom sort order without specifying a routine?
I think that's a case of Broken By Design. Consider the following snippet:
my $a = Point.new(:3x, :2y);
my $b = Point.new(:2x, :4y);
say &infix:<cmp>.WHICH;
say $a cmp $b;
{
multi sub infix:<cmp>(Point $a, Point $b) { $a.y cmp $b.y || $a.x cmp $b.x }
say &infix:<cmp>.WHICH;
say $a cmp $b;
}
say &infix:<cmp>.WHICH;
say $a cmp $b;
The definition of the new multi candidate will generate a new proto sub that is only visible lexically. As the sort method is defined in the setting (conceptionally, an enclosing scope), it won't see your new multi candidate.
It might be possible to make sort look up &infix:<cmp> dynamically instead of lexically, though I suspect such a change would have to wait for 6.e even if we decided that's something we want to do, which isn't a given.
As a workaround, you could do something like
constant CMP = &infix:<cmp>;
multi sub infix:<cmp>(Point $a, Point $b) { ... }
BEGIN CMP.wrap(&infix:<cmp>);
for now, though I wouldn't necessarily recommend it (messing with global state considered harmful, and all that jazz)...
The cmp that is being used is the cmp that is in lexical scope within sort, not the one you have defined. If you change a few lines to:
multi sub infix:<cmp>(Point $a, Point $b) {
say "Hey $a $b";
$a.y cmp $b.y || $a.x cmp $b.x
}
my @p = Point.new(:3x, :2y), Point.new(:2x, :4y), Point.new(:1x, :1y);
say @p.sort( { $^b cmp $^a } );
Since the cmp that is being used is the one that is in actual lexical scope, it gets called correctly printing:
Hey (2,4) (3,2)
Hey (1,1) (2,4)
Hey (1,1) (3,2)
((2,4) (3,2) (1,1))
As was required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With