9
use List::MoreUtils 'uniq';
print join ", ", sort uniq ("b", "a", "a");

results in Argument "a" isn't numeric in sort at ...

print join ", ", uniq sort ("b", "a", "a");

works as expected.

print join ", ", sort {$a cmp $b} uniq ("b", "a", "a");

works too - but what is the problem with the first example?

André
  • 405
  • 5
  • 16
  • 2
    My guess is that the `sort uniq ...` is interpreted as the [`sort SUBNAME LIST`](https://perldoc.perl.org/functions/sort.html) syntax where `SUBNAME` is `uniq` instead of the intended `sort LIST` syntax. You can achieve the latter using `sort @{[uniq ("b", "a", "a")]}` – Håkon Hægland Aug 10 '19 at 19:05
  • 1
    @HåkonHægland Why isn't `sort (uniq @list)` parsed as `sort LIST` syntax when `sort ("a","b","c")` is? Is this just a strange quirk of the Perl parser? – Jim Garrison Aug 10 '19 at 19:10
  • @JimGarrison In `sort (uniq @ary)` there is no reason for the interpreter to see that `uniq` is anything other than a bareword in that list (and is thus out of place). Thus `sort (uniq(@ary));` works – zdim Aug 10 '19 at 19:36
  • @Jim Garrison, `sort`'s syntax is what's quirky. Specifically, it's very ambigous, so Perl needs to perform all kinds of guesses. – ikegami Aug 10 '19 at 19:36
  • 1
    Here's something insane: while `sort(uniq(@ary))` works, `sort(uniq (@ary))` does not. The only difference is the space I added between `uniq` and `(@ary)`. I always thought whitespace was not semantically significant! – joanis Aug 10 '19 at 19:49
  • @joanis It's the extra parens that don't count so `(word (@ary))` is no different from `(word @ary)`. It's a good point though, that li'l space does count here (under `sort`) – zdim Aug 10 '19 at 19:51

1 Answers1

7

That code falls under the sort invocation of

sort SUBNAME LIST

...
If SUBNAME is specified, it gives the name of a subroutine that returns an integer less than, equal to, or greater than 0 , depending on how the elements of the list are to be ordered.

The uniq in the first example is taken as a bareword that specifies the name of the sub to use for sorting and qw(b a a) is the list to sort -- you aren't uniq-ing the list (so to speak) but are using uniq as a sorting function for that list.

The error message comes as a sorting function needs to return a number and uniq returns strings.

You've discovered one way to make it work, and can also use the unary +

say for sort +uniq(@ary);    # or: say for sort + uniq @ary;

since what follows + is treated as an expression, so sort gets the evaluated list. See this post and this post for discussion of such uses of the unary +.

Or use parens through and through

say for sort (uniq(@ary));

Here the inner pair is also necessary since with sort (uniq @ary) the uniq is interpreted as a bareword in that list, thus out of place. Note that sort (uniq (@ary)) won't work since extra parens don't matter and we again have a bareword; so that space matters here!

This trickiness is due to sort's flexible (ambiguous) interface, taking a LIST optionally preceded by a bareword, which need be a sub name. It ends up relying on the interpreter to sort out some of that and then we have to be precise.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • @ikegami Yes, thank you (just posted a comment with that but forgot it here), added to answer – zdim Aug 10 '19 at 19:49