让Perl6调用正确的子类型(子集)专用多重子例程

10

我用perl6的subset命令和一些针对这些类型进行特化的多个子例程构建了一个类型层次结构。当发生多重分派时,如何使最窄的子类型专门化的子例程具有最高优先级?

这是简化代码:

#! /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 
2个回答

10

由于所有候选项都是相等的,所以将选择与其余约束条件相匹配(或缺乏约束条件)的第一个候选项。这时,指定多个候选项的顺序变得很重要。如果您按照以下顺序指定它们:

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

以下内容将会按照您的期望执行:

check(32);   # positive & even

5
主要问题在于子集并不是实际的类型,它只是一种约束条件。 如果您这样做
say :(PositiveEven $value).perl;
say :(Positive $value).perl;
say :(Int $value).perl;

你将获得:
:(Int $value where { ... })
:(Int $value where { ... })
:(Int $value)

虽然最新的一个显然不同,但是另外两个在签名上没有区别,因此找到的第一个会被使用。你需要将它们声明为类或者找到另一种通过签名进行区分的方法,或者在子程序内部使用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"
}

这将按预期返回正数和偶数。您甚至不需要将最后一个子参数定义为PositiveEven,但保留它以供信息目的也可以。


1
谢谢。现在它工作了。然而,Postive的子程序必须知道自身派生的子类型,这会导致代码无法扩展。类和继承也能够完成这项工作,但由于某些原因,我不能使用它们。不使用类,有可能让它既能运行又具备可扩展性吗? - lovetomato
@lovetomato 你可以尝试使用角色;你也可以在签名中使用它们。我脑海中暂时想不到其他的了...也许可以使用参数化角色代替子集...但我需要检查一下... - jjmerelo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接