'Raku pop() order of execution
Isn't order of execution generally from left to right in Raku?
my @a = my @b = [9 , 3];
say (@a[1] - @a[0]) == (@b[1] R- @b[0]); # False {as expected}
say (@a.pop() - @a.pop()) == (@b.pop() R- @b.pop()); # True {Huh?!?}
This is what I get in Rakudo(tm) v2020.12 and 2021.07. The first 2 lines make sense, but the third I can not fathom.
Solution 1:[1]
It is.
But you should realize that the minus infix operator is just a subroutine under the hood, taking 2 parameters that are evaluated left to right. So when you're saying:
$a - $b
you are in fact calling the infix:<-> sub:
infix:<->($a,$b);
The R meta-operator basically creates a wrap around the infix:<-> sub that reverses the arguments:
my &infix:<R->($a,$b) = &infix:<->.wrap: -> $a, $b { nextwith $b, $a }
So, if you do a:
$a R- $b
you are in fact doing a:
infix:<R->($a,$b)
which is then basically a:
infix:<->($b,$a)
Note that in the call to infix:<R-> in your example, $a become 3, and $b becomes 9 because the order of the arguments is processed left to right. This then calls infix:<->(3,9), producing the -6 value that you would also get without the R.
It may be a little counter-intuitive, but I consider this behaviour as correct. Although the documentation could probably use some additional explanation on this behaviour.
Solution 2:[2]
Let me emulate what I assumed was happening in line 3 of my code prefaced with @a is the same as @b is 9, 3 (big number then little number)
(@a.pop() - @a.pop()) == (@b.pop() R- @b.pop())
(3 - 9) == (3 R- 9)
( -6 ) == ( 6 )
False
...That was my expectation. But what raku seems to be doing is
(@a.pop() - @a.pop()) == (@b.pop() R- @b.pop())
#R meta-op swaps 1st `@b.pop()` with 2nd `@b.pop()`
(@a.pop() - @a.pop()) == (@b.pop() - @b.pop())
(3 - 9) == (3 - 9)
( -6 ) == ( -6 )
True
The R in R- swaps functions first, then calls the for values. Since they are the same function, the R in R- has no practical effect.
Side Note: In fuctional programming a 'pure' function will return the same value every time you call it with the same parameters. But pop is not 'pure'. Every call can produce different results. It needs to be used with care.
Solution 3:[3]
The R meta op not only reverses the operator, it will also reverse the order in which the operands will be evaluated.
sub term:<a> { say 'a'; '3' }
sub term:<b> { say 'b'; '9' }
say a ~ b;
a
b
ab
Note that a happened first.
If we use R, then b happens first instead.
say a R~ b;
b
a
ba
The problem is that in your code all of the pop calls are getting their data from the same source.
my @data = < a b a b >;
sub term:<l> { my $v = @data.shift; say "l=$v"; return $v }
sub term:<r> { my $v = @data.shift; say "r=$v"; return $v }
say l ~ r;
l=a
r=b
ab
say l R~ r;
r=a
l=b
ab
A way to get around that is to use the reduce meta operator with a list
[-](@a.pop, @a.pop) == [R-](@a.pop, @a.pop)
Or in some other way make sure the pop operations happen in the order you expect.
You could also just use the values directly from the array without using pop.
[-]( @a[0,1] ) == [R-]( @a[2,3] )
Solution 4:[4]
Let me emulate what happens by writing the logic one way for @a then manually reversing the operands for @b instead of using R:
my @a = my @b = [9 , 3];
sub apop { @a.pop }
sub bpop { @b.pop }
say apop - apop; # -6
say bpop - bpop; # -6 (operands *manually* reversed)
This not only appeals to my sense of intuition about what's going on, I'm thus far confused why you were confused and why Liz has said "It may be a little counter-intuitive" and you've said it is plain unintuitive!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Elizabeth Mattijsen |
| Solution 2 | Elizabeth Mattijsen |
| Solution 3 | |
| Solution 4 |
