perl6 min and max of mixed Str and Int arguments
Asked Answered
R

2

8

What type gets converted first for min and max routine when the arguments contain mixture of Str and Int ?

To exit type 'exit' or '^D'
> say ("9", "10").max
9
> say ("9", "10").max.WHAT
(Str)
> say (9, "10").max
9
> say (9, "10").max.WHAT
(Int)                     # if convert to Int first, result should be 10
> say ("9", 10).max
9
> say ("9", 10).max.WHAT
(Str)                     # if convert to Str first, result should be 9
> say (9, "10").min
10
> say (9, "10").min.WHAT
(Str)                     # does min and max convert Str or Int differently?

If min or max converts arguments to be the type of the first argument, the results here are still inconsistent.

Roxy answered 21/7, 2019 at 19:4 Comment(0)
F
5

Well, jnthn has answered. His answers are always authoritative and typically wonderfully clear and succinct too. This one is no exception. :) But I'd started so I'll finish and publish...

A search for "method min" in the Rakudo sources yields 4 matches of which the most generic is a match in core/Any-iterable-methods.pm6.

It might look difficult to understand but nqp is actually essentially a simple subset of P6. The key thing is it uses cmp to compare each value that is pulled from the sequence of values being compared against the latest minimum (the $pulled cmp $min bit).

Next comes a search for "sub infix:<cmp>" in the Rakudo sources. This yields 14 matches.

These will all have to be looked at to confirm what the source code shows for comparing these various types of value. Note also that the logic is pairwise for each pair which is slightly weird to think about. So if there's three values a, b, and c, each of a different type, then the logic will be that a is the initial minimum, then there'll be a b cmp a which will be whatever cmp logic wins for that combination of types in that order, and then c cmp d where d is whichever won the b cmp a comparison and the cmp logic will be whatever is suitable to that pair of types in that order.

Let's start with the first one -- the match in core/Order.pm6 -- which is presumably a catchall if none of the other matches are more specific:

  • If both arguments of cmp are numeric, then comparison is a suitable numeric comparison (eg if they're both Ints then comparison is of two arbitrary precision integers).

  • If one argument is numeric but not the other, then -Inf and Inf are sorted to the start and end but otherwise comparison is done after both arguments are coerced by .Stringyfication.

  • Otherwise, both arguments are coerced by .Stringyfication.

So, that's the default.

Next one would have to go thru the individual overloads. For example, the next one is the cmp ops in core/allomorphs.pm6 and we see how for allomorphic types (IntStr etc.) comparison is numeric first, then string if that doesn't settle it. Note the comment:

we define cmp ops for these allomorphic types as numeric first, then Str. If you want just one half of the cmp, you'll need to coerce the args

Anyhoo, I see jnthn's posted yet another great answer so it's time to wrap this one. :)

Florentinoflorenza answered 21/7, 2019 at 20:44 Comment(1)
Thank you very much raiph ! I think I should start to look into Rakudo sources which may help answer many of my questions in the future !Roxy
S
11

Both min and max use the cmp infix operator to do the comparisons. If the types differ, then this logic is used (rewritten slightly to be pure Perl 6, whereas the real one uses an internals shortcut):

multi sub infix:<cmp>($a, $b) {
    $a<> =:= $b<>
      ?? Same
      !! a.Stringy cmp b.Stringy
}

Effectively, if the two things point to the exact same object, then they are the Same, otherwise stringify both and then compare. Thus:

say 9 cmp 10;      # uses the (Int, Int) candidate, giving Less
say "9" cmp "10";  # uses the (Str, Str) candidate, giving More
say 9 cmp "10";    # delegates to "9" cmp "10", giving More
say "9" cmp 10;    # delegates to "9" cmp "10", giving More

The conversion to a string is done for the purpose of comparison (as an implementation detail of cmp), and so has no impact upon the value that is returned by min or max, which will be that found in the input list.

Stearoptene answered 21/7, 2019 at 20:28 Comment(1)
Thank you very much Jonathan Worthington ! Clear and to-the-point !Roxy
F
5

Well, jnthn has answered. His answers are always authoritative and typically wonderfully clear and succinct too. This one is no exception. :) But I'd started so I'll finish and publish...

A search for "method min" in the Rakudo sources yields 4 matches of which the most generic is a match in core/Any-iterable-methods.pm6.

It might look difficult to understand but nqp is actually essentially a simple subset of P6. The key thing is it uses cmp to compare each value that is pulled from the sequence of values being compared against the latest minimum (the $pulled cmp $min bit).

Next comes a search for "sub infix:<cmp>" in the Rakudo sources. This yields 14 matches.

These will all have to be looked at to confirm what the source code shows for comparing these various types of value. Note also that the logic is pairwise for each pair which is slightly weird to think about. So if there's three values a, b, and c, each of a different type, then the logic will be that a is the initial minimum, then there'll be a b cmp a which will be whatever cmp logic wins for that combination of types in that order, and then c cmp d where d is whichever won the b cmp a comparison and the cmp logic will be whatever is suitable to that pair of types in that order.

Let's start with the first one -- the match in core/Order.pm6 -- which is presumably a catchall if none of the other matches are more specific:

  • If both arguments of cmp are numeric, then comparison is a suitable numeric comparison (eg if they're both Ints then comparison is of two arbitrary precision integers).

  • If one argument is numeric but not the other, then -Inf and Inf are sorted to the start and end but otherwise comparison is done after both arguments are coerced by .Stringyfication.

  • Otherwise, both arguments are coerced by .Stringyfication.

So, that's the default.

Next one would have to go thru the individual overloads. For example, the next one is the cmp ops in core/allomorphs.pm6 and we see how for allomorphic types (IntStr etc.) comparison is numeric first, then string if that doesn't settle it. Note the comment:

we define cmp ops for these allomorphic types as numeric first, then Str. If you want just one half of the cmp, you'll need to coerce the args

Anyhoo, I see jnthn's posted yet another great answer so it's time to wrap this one. :)

Florentinoflorenza answered 21/7, 2019 at 20:44 Comment(1)
Thank you very much raiph ! I think I should start to look into Rakudo sources which may help answer many of my questions in the future !Roxy

© 2022 - 2024 — McMap. All rights reserved.