In Perl 6, there is an infix operator called `cmp`. Despite its simple name and some connotations with its counter partner in Perl 5, its semantic is not trivial.

From the documentation, we read:

*Generic, “smart” three-way comparator.*

*Compares strings with string semantics, numbers with number semantics, Pair objects first by key and then by value etc.*

As we have access to the source codes, let us directly look inside and allow us to begin with strings, so go to src/core/Str.pm.

multi sub infix:<cmp>(Str:D \a, Str:D \b --> Order:D) { ORDER(nqp::cmp_s(nqp::unbox_s(a), nqp::unbox_s(b))) } multi sub infix:<cmp>(str $a, str $b --> Order:D) { ORDER(nqp::cmp_s($a, $b)) }

There is a method operating two objects of the `Str` type and another method for the lower-cased type `str`, which is a native type, which we skip for now; just look at its definition in src/core/natives.pm:

my native str is repr('P6str') is Str { }

For the Perl 6 type, the objects are first converted to native strings via `nqp::unbox_s`.

Then both methods delegate the comparison to the `nqp::cmp_s` function. It returns 1, 0, or -1, which is fine in NQP but not enough for Perl 6, where the result should be of the `Order` type—you can see the expected return type `Order:D` in the signature of the methods.

Go to src/core/Order.pm to see that the `Order` type is an enumeration with the above three values:

my enum Order (:Less(-1), :Same(0), :More(1));

In the same file, there is a function that acts as a constructor coercing an integer to `Order`:

sub ORDER(int $i) { nqp::iseq_i($i,0) ??Same!! nqp::islt_i($i,0) ??Less!!More}

So, the result of `cmp` is either `Same`, or `Less`, or `More`.

We covered the hardest part already. The rest of the smartness of the `cmp` operator is due to multiple dispatching.

For example, for the two given integers, the following functions are triggered (also defined in src/core/Order.pm):

multi sub infix:<cmp>(Int:D \a, Int:D \b) { ORDER(nqp::cmp_I(nqp::decont(a), nqp::decont(b))) } multi sub infix:<cmp>(int $a, int $b) { ORDER(nqp::cmp_i($a, $b)) }

Here, there is not much difference from the string implementation. You may notice the different suffixes in the NQP methods.

Then, step by step, variety rises. For example, integers and rationals:

multi sub infix:<cmp>(Int:D \a,Rational:D\b) { a.isNaN || b.isNaN ?? a.Num cmp b.Num !! a <=> b } multi sub infix:<cmp>(Rational:D\a, Int:D \b) { a.isNaN || b.isNaN ?? a.Num cmp b.Num !! a <=> b }

Again, the implementation is simple but of course it is different from what was needed for two integers or two strings.

It gets more complicated for `Real` numbers:

multi sub infix:<cmp>(Real:D \a, Real:D \b) { (nqp::istype(a, Rational) && nqp::isfalse(a.denominator)) || (nqp::istype(b, Rational) && nqp::isfalse(b.denominator)) ?? a.Bridge cmp b.Bridge !! a === -Inf || b === Inf ?? Less !! a === Inf || b === -Inf ?? More !! a.Bridge cmp b.Bridge }

I leave parsing the algorithms to the reader as an exercise but would like to pay attention to the use of the `Bridge` method, which is a polymorphic method that we already saw as part of the `Int` type.

There are separate methods for comparing complex numbers, dates, lists, ranges, and even version numbers (which looks quite complicated, by the way, see it in src/core/Version.pm).

At the bottom (or at the top, as you define what is more and less important—base or children classes), there are a few methods that deal with `Mu`:

proto sub infix:<cmp>(Mu $, Mu $) is pure {*} multi sub infix:<cmp>(\a, \b) { nqp::eqaddr(a,b) ?? Same !! a.Stringy cmp b.Stringy } multi sub infix:<cmp>(Real:D \a, \b) { a === -Inf ?? Less !! a === Inf ?? More !! a.Stringy cmp b.Stringy } multi sub infix:<cmp>(\a, Real:D \b) { b === Inf ?? Less !! b === -Inf ?? More !! a.Stringy cmp b.Stringy }

That’s all for today, see you tomorrow!

## 2 thoughts on “33. The cmp infix in Perl 6”