Reducing comparison operators

Brad Gilbert saw my Effective Perl post about script runs (a stable feature in v5.32) and offered this Raku version on Twitter:

# Perl 5:  /(*script_run:(\d+))/
/ \d+  <?{ [eq] $/.Str.uniprops('script') }> /

Bascially, it matches \d+ then immediately uses an assertion that looks that the thing it just matched, $/, has characters with all the same Unicode properties.

He then defines a new regex to wrap it all up:

my regex script-run ($r) {
  $r
  <?{ [eq] $/.Str.uniprops('script') }>
}

/ <script-run( /\d+/ )> /

That’s fine. The Raku grammars are really cool and amazingly flexible and is a language within the language (well, “slang”).

The thing I find delightful about this Brad’s use of the reduction operator, [...] with a comparison operator. So, I wrote this little program in Raku (in repl.it):

my $n = 200.rand.Int;

if [<] 37, $n, 137 {
    say "$n is between 37 and 137";
    }
else {
    say "$n is not between 37 and 137";
    }

This works, and had I thought about it I might have expected it to work. Many (not all) of the math operators don’t care about the order of operations, so 2 + 3 + 5 is (2 + 3) + 5 is 2 + (3 + 5). Some operations do care, like exponentiation: 4**3**2 is 4**(3**2) and is not (4**3)**2. The reduction operator isn’t what you might expect from most examples; it doesn’t just run the operation with the first two elements and replace them with a single value. It knows what operations to do first.

But, what’s the result of a comparison operator? What does the reduction operator do then?

Raku has “chained” comparisons, but what you write is not what Raku is actually doing (like any good higher-level language):

37 < $n < 137

which is the same as doing each comparison on its own and logically combining the results:

37 < $n and $n < 137

Another way to write this is with the reduction operator:

[<] 37, $n, 137

And it all works out. No loops and short.

One comment

  1. One difference: [<] isn't lazy / won't short-circuit:

    #!/usr/bin/env raku
    sub one { say "ONE"; 1 }
    sub ten { say "TEN"; 10 }

    # ONE
    # False
    say so ( one() < 5 and 5 < 3 and 3 < ten() );

    # ONE
    # TEN
    # False
    say so ( [<] one(), 5, 3, ten() );

Leave a Reply

Your email address will not be published. Required fields are marked *