9

* > 20 && * %% 5 used in grep seems wrong, does is equal to a WhateverCode lambda that takes 2 arguments? As this explain on SO

> my @a = 1,12,15,20,25,30,35,37;

> @a.grep: * > 20 && * %% 5 # The result seems strange, expected (25 30 35)
(15 20 25 30 35)

> @a.grep: * %% 5 && * > 20
(25 30 35 37)

> @a.grep: { $_>20 && $_ %% 5 }
(25 30 35)

> @a.grep: all(* > 20, * %% 5)
(25 30 35)

> @a.grep: -> $a { all($a > 20, $a %% 5) }
(25 30 35)

> @a.grep: -> $a {$a > 20 && $a %% 5}
(25 30 35)
chenyf
  • 5,048
  • 1
  • 12
  • 35
  • 1
    Strangely, if you reverse operands of `&&` it works: `@a.grep: * %% 5 && * > 20` – Håkon Hægland May 10 '18 at 13:35
  • 1
    @Håkon Hægland No, it seems that it evaluate to the right of `&&` as result: `* %% 5 && * > 20` is `* > 20` – chenyf May 10 '18 at 13:44
  • @raiph maybe you are right,it is a bug – chenyf May 10 '18 at 15:02
  • Deleted again. So all your cases except the first two make sense, right? The use of two `*` in your 4th case is two uses of one `*`, because `,` separates expressions. So you're only using two `*` in a way one would expect to create a two arg lambda in the first two cases. Using occam's razor, I note that the first two cases seem to be just doing the equivalent of a one arg lambda that ignores the left hand of the `&&`. Also, Brian's guess doesn't explain your first case. – raiph May 10 '18 at 15:33
  • Quoting my previous comment: "So you're only using two `*` in a way one would expect to create a two arg lambda in the first two cases." So, our expectation was the problem. See your and my answer to see that code similar to your first two cases clearly produces one arg lambdas -- and my answer for why. – raiph May 10 '18 at 17:18

2 Answers2

8

Golfed

my &or  = * == 1 || * == 2 ;
my &and = * == 1 && * == 2 ;

say .signature, .(1), .(2)
  for &or, ∧

displays:

(;; $whatevercode_arg_1 is raw)TrueFalse
(;; $whatevercode_arg_4 is raw)FalseTrue

I still don't know what's going on [ed: that is, I didn't at the time I wrote this paragraph; I kept what I wrote in this answer as the mystery unfolded], but it's clear that the signature is for just one arg and the result is as per just the right hand expression for the &and and the left hand for the &or which means the code doesn't seem to have, er, left the result that's, er, right. Investigation continues... (and no, I'm not det remiker).

Mystery solved

So, it looks like the logical ops (&&, ||, and, or, etc.) don't do Whatever-currying. Which is fair enough given that "not all operators and syntactic constructs curry * (or Whatever-stars) to WhateverCode". Logical, even, given their nature. They probably ought to be added to the table of exceptions on that page though.

In the meantime, operators like == do Whatever curry. Again, that's fair enough given "subexpressions may impose their own Whatever star rules".

So it makes sense that &or and &and turn in to...

Aha! Got it. The * == 1 and * == 2 are evaluated at compile-time and turn into WhateverCodes. As WhateverCodes they are just bits of code. They are defined. They are True. (This ignores calling them at run-time.) Then along comes the && and evaluates to the right hand WhateverCode. (The || would evaluate to its left hand WhateverCode.)

Hence the behavior we see.

A solution

Per prompting by @HåkonHægland, the code that would work is therefore code that doesn't rely on logical ops Whatever-currying, i.e.:

my @a = 1,12,15,20,25,30,35,37;

say @a.grep: { $_ > 20 && $_ %% 5 } # (25 30 35)

Now what?

Now we have to figure out what doc edits to propose...

Actually, before we do that, confirm that logical ops are supposed to not Whatever-curry...

And to start that ball rolling, I just trawled the results of a search for TimToady comments on #perl6 about "currying" (there were none on #perl6-dev), looking for ones pertinent to the case we have here.

First, one from 2017 that's arguably relevant to any doc edits:

the design docs actually try to avoid the word "currying" ... but it's hard to get people to use words differently than they do

Next, one from 2015 about && and || and such:

|| and && and such are really control flow operators, turned rather rapidly into 'if' and 'unless' ... those ops can be curried with .assuming, I assume

And finally a couple from 2010 that also seem potentially important (though perhaps one or more are no longer applicable?):

all operators autocurry a WhateverCode, whether or not they curry a Whatever

I think we can keep the current mechanism as a fallback for operators that still want to curry at run time

raiph
  • 31,607
  • 3
  • 62
  • 111
2
> my $d = * + * + * 
> $d.arity
3
> my $e = * == 1 || * == 2 || * == 3 
> $e.arity
1

as the doc say:

Returns the minimum number of positional arguments that must be passed in order to call the code object.

so I think the all three star in * == 1 || * == 2 || * == 3 is the same thing.

> my $e = * == 1 && * == 2 && * > 3 
> $e(1)
False
> $e(2)
False
> $e(3)
False
> $e(4)
True
chenyf
  • 5,048
  • 1
  • 12
  • 35
  • Yes. My answer explains why. – raiph May 10 '18 at 17:14
  • yw. :) It's so obvious in hindsight. Absolutely baffling till then. That "Aha" I wrote literally happened as I was writing that sentence. (I guess it's not surprising it "literally" happened while I was *writing*, but you know what I mean.) – raiph May 10 '18 at 17:37