Where did my Perl 6 operator go after I defined a more specific multi?
Asked Answered
C

1

14

I'm playing with this little thing where a set can act as its complement by flipping around the tests. In order to make that work, I create special versions of the membership operators.

class Complement {
    has $.set;
    }

multi infix:<∈> ( $a, Complement:D $c ) { $a ∉ $c.set   }
multi infix:<∉> ( $a, Complement:D $c ) { $a ∈ $c.set }

my $set = (1, 2, 3).Set;
my $c = Complement.new: set => $set;

put 3 ∈ $set;
put 4 ∈ $c;

With my definition of infix:<∉> the other, more general one seems to have disappeared. There are no other candidates:

True
Cannot resolve caller infix:<∉>(Int, Set); none of these signatures match:
    ($a, Complement:D $c)
  in sub infix:<∈> at /Users/brian/Desktop/complement.p6 line 11
  in block <unit> at /Users/brian/Desktop/complement.p6 line 18

What do I need to do to retain previous definitions with different signatures?

Curtal answered 16/2, 2018 at 1:50 Comment(1)
As of github.com/rakudo/rakudo/commit/16b57af549 this will now work, as multi infix:<∉> is now also a multi in core.Hadrian
S
14

This is because &infix:<∈> is a multi, while &infix:<∉> is not:

$ perl6 -e '&infix:<∈>.candidates.head.multi.say'
True
$ perl6 -e '&infix:<∉>.candidates.head.multi.say'
False

When you defined your multi &infix:<∉>, you shadowed only &infix:<∉> from core, and that's why you see only your candidate.

We can probably detect such a situation and require the user to provide explicit proto to clarify what they meant. I filed that as R#1530. And I'd say such differences that one op is a multi while another is an only is part of a larger issue of having consistent core; I added it as a comment on RT#130020 and mentioned the problem in our docs on D#1783.

You could define your custom op like so (if the suggestion in R#1530 is implemented, the proto would be required; currently it isn't):

proto infix:<∉> (|) {*}
multi infix:<∉> ($a, Complement:D $c) { $a ∈ $c.set }
multi infix:<∉> (|c) { &CORE::infix:<∉>(|c) }

I can only guess what you're trying to code, but thought I'd mention it, for you to account for it in your creation: negated ops are defined as not A (op) B, not A (not(op)) B. This small difference has impact when it comes to Junctions:

$ perl6 -e 'say 2 ≠ 2|4'
False

If it were defined as a negated op applied to each operand, the above would be equivalent to (2 ≠ 2 or 2 ≠ 4) and would always be True, which is often not what the programmer would've meant, hence why it's instead computed as not (2 == 2 or 2 == 4)


To reply to Jarrod's comments on OP: subs (including grammar mutations done by declaring custom operators) are lexical, so if you wrapped the declaration into a BEGIN block, the custom op will be scoped to just that block.

And the reason it appeared to work in REPL is because there's actually a bug (RT#131900) where custom ops are lost between REPL lines, so just as with the BEGIN block, by the time the call came with args destined to the core candidates, the custom multi was no longer in scope.

Sulphurbottom answered 16/2, 2018 at 13:9 Comment(2)
Ah wow, my bad. I actually thought I explicitly tested to see that both custom ops were working. I guess I was wrong.Evaluate
@JarrodFunnell they work in a sense that there's no crash, but the candidate it goes through is the core one, so the output is "True/False" rather than the expected "True/True"Sulphurbottom

© 2022 - 2024 — McMap. All rights reserved.