10

I have a type hierarchy constructed with perl6's subset command and some multi subs specialized on these type. How to give the highest precedence to the sub specialized by the most narrow subtype when multi dispatch happens?

Here is the simplified code:

#! /usr/bin/env perl6

use v6.c;

proto check($value) { * }

subset Positive of Int where * > 0;

subset PositiveEven of Positive where * %% 2;

multi check(Int $value) {
    say "integer"
}

multi check(Positive $value) {
    say "positive"
}

multi check(PositiveEven $value) {
    say "positive & even"
}

# example:
check(32);

# expected output:
#   positive & even

# actual output:
#   positive 
lovetomato
  • 903
  • 5
  • 11

2 Answers2

10

Since all candidates are equally tight, it will take the first one that matches the rest of the constraint (or lack thereof). This is when the order in which the multi candidates are specified, becomes important. If you would have specified them in this order:

multi check(PositiveEven $value) { say "positive & even" }
multi check(Positive $value) { say "positive" }
multi check(Int $value) { say "integer" }

The following will do what you expect:

check(32);   # positive & even
Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
5

The main problem is that Subsets are not actually types, just constraints. If you do

say :(PositiveEven $value).perl;
say :(Positive $value).perl;
say :(Int $value).perl;

You will obtain

:(Int $value where { ... })
:(Int $value where { ... })
:(Int $value)

While the latest one is clearly different, the two others have no difference regarding signature, and thus the first one found is used. You will need either to declare them as classes or find another way to differentiate them by signature, or inside the sub itself using nextsame

proto check($value) { * }

subset PositiveEven of UInt where * %% 2;

multi check(Int $value) {
    say "integer"
}

multi check(UInt $value) {
    if $value ~~ PositiveEven {
    nextsame;
    }
    say "positive"
}

multi check(PositiveEven $value) {
    say "positive & even"
}

This will return positive & even as expected. You don't even need to define the last sub's arg as PositiveEven, but it's OK to leave it there for informative purposes.

Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
jjmerelo
  • 22,578
  • 8
  • 40
  • 86
  • 1
    Thank you. It works now. However the sub for Postive has to know the subtype derived from itself, which causes the code not extensible. Class and inheritance do the job too, but for some reasons I can't use them. Is it possible to make it run as well as be extensible without class? – lovetomato Jan 23 '19 at 11:55
  • @lovetomato you might try roles; you can use them in signatures too. I can't think of anything else off the top of my head... Maybe use parametrized roles instead of subsets... But I would have to check that... – jjmerelo Jan 23 '19 at 12:57