首页 > 解决方案 > 别名为常量时无法解析签名

问题描述

作为这个关于在单个程序中使用不同 API 的问题的后续,Liz Mattijsen 建议使用常量。现在这是一个不同的用例:让我们尝试创建一个multi按 API 版本区分的,如下所示:

class WithApi:ver<0.0.1>:auth<github:JJ>:api<1>  {}
my constant two = my class WithApi:ver<0.0.1>:auth<github:JJ>:api<2> {}

multi sub get-api( WithApi $foo where .^api() == 1 ) {
    return "That's version 1";
}

multi sub get-api( WithApi $foo where .^api() == 2 ) {
    return "That's version deuce";
}

say get-api(WithApi.new);
say two.new.^api;
say get-api(two.new);

我们在第二个版本中使用了一个常量,因为两者不能一起在一个符号空间中。但这会产生这个错误:

That's version 1
2
Cannot resolve caller get-api(WithApi.new); none of these signatures match:
    (WithApi $foo where { ... })
    (WithApi $foo where { ... })
  in block <unit> at ./version-signature.p6 line 18

所以say two.new.^api;返回正确的api版本,调用者是get-api(WithApi.new),所以$foo有正确的类型和正确的API版本,但是multi没有被调用?我在这里缺少什么吗?

标签: rakudispatch

解决方案


TL;DR JJ 的答案是一个运行时where子句,它在关注的参数上调用一对方法。其他人的答案都做同样的工作,但使用提供更好检查和更好性能的编译时构造。这个答案将我的观点与 Liz 和 Brad 的观点融为一体。

JJ 答案的主要优点和缺点

在 JJ 的回答中,所有逻辑都包含在一个where子句中。相对于其他所有人的答案,这是它相对于解决方案的唯一优势;它根本没有添加任何 LoC。

JJ 的解决方案有两个明显的弱点:

  • 检查和调度where参数子句的开销发生在运行时1。即使谓词不是,这也是代价高昂的。在 JJ 的解决方案中,谓词昂贵的,使事情变得更糟。总而言之,使用多重分派时的最坏情况下的开销是所有s中使用的所有子句的总和wheremulti

  • 在代码中,每个变体where .^api() == 1 && .^name eq "WithApi"的 43 个字符中有 42 个重复。multi相比之下,非where子句类型约束要短得多,并且不会掩盖差异。当然,JJ 可以声明subsets 具有类似的效果,但这会消除他们解决方案的唯一优势,而不会修复其最重要的弱点。

附加编译时元数据;在多次调度中使用它

在特别讨论 JJ 的问题之前,这里有一些通用技术的变体:

role Fruit {}                             # Declare metadata `Fruit`

my $vegetable-A = 'cabbage';
my $vegetable-B = 'tomato' does Fruit;    # Attach metadata to a value

multi pick (Fruit $produce) { $produce }  # Dispatch based on metadata

say pick $vegetable-B;                    # tomato

再次相同,但参数化:

enum Field < Math English > ;

role Teacher[Field] {}                    # Declare parameterizable metadata `Teacher`

my $Ms-England  = 'Ms England'; 
my $Mr-Matthews = 'Mr Matthews';

$Ms-England  does Teacher[Math];
$Mr-Matthews does Teacher[English];

multi field (Teacher[Math])    { Math }
multi field (Teacher[English]) { English }

say field $Mr-Matthews;                   # English

我用 arole作为元数据,但那是偶然的。关键是要有可以在编译时附加的元数据,并且它有一个类型名称,以便可以在编译时建立调度解决方案候选者。

JJ 运行时答案的编译时元数据版本

解决方案是声明元数据并将其附加到 JJ 的类中。

布拉德解决方案的变体:

class WithApi1 {}
class WithApi2 {}

constant one = anon class WithApi:ver<0.0.1>:auth<github:JJ>:api<1> is WithApi1 {}

constant two = anon class WithApi:ver<0.0.1>:auth<github:JJ>:api<2> is WithApi2 {}

constant three = anon class WithApi:ver<0.0.2>:api<1> is WithApi1 {} 

multi sub get-api( WithApi1 $foo ) { "That's api 1" }

multi sub get-api( WithApi2 $foo ) { "That's api deuce" }

say get-api(one.new); # That's api 1
say get-api(two.new); # That's api deuce
say get-api(three.new); # That's api 1

另一种方法是编写一个可参数化的元数据项:

role Api[Version $] {}

constant one = anon class WithApi:ver<0.0.1>:auth<github:JJ>:api<1> does Api[v1] {}

constant two = anon class WithApi:ver<0.0.1>:auth<github:JJ>:api<2> does Api[v2] {}

constant three = anon class WithApi:ver<0.0.2>:api<v1> does Api[v1] {} 

multi sub get-api( Api[v1] $foo ) { "That's api 1" }

multi sub get-api( Api[v2] $foo ) { "That's api deuce" }

say get-api(one.new); # That's api 1
say get-api(two.new); # That's api deuce
say get-api(three.new); # That's api 1

版本匹配范围

在下面的评论中,JJ 写道:

如果您使用where子句,您可以让multis 在最多多个版本上分派(因此无需为每个版本创建一个)

此答案中涵盖的role解决方案还可以通过添加另一个角色来分派版本范围:

role Api[Range $ where { .min & .max ~~ Version }] {}

...

multi sub get-api( Api[v1..v3] $foo ) { "That's api 1 thru 3" }

#multi sub get-api( Api[v2] $foo ) { "That's api deuce" }

这将显示That's api 1 thru 3所有三个呼叫。v2如果第二个 multi 未注释,则调用优先。

请注意,get-api尽管角色签名包含一个where子句,但仍会检查例程调度并在编译时解析候选者。这是因为运行角色where子句的运行时间是在get-api程序编译期间;当get-api例程被调用时,角色的where子句不再相关。

脚注

1多重约束中,拉里写道:

对于 6.0.0 ...从where子句推断出的任何结构类型信息都将被忽略 [在编译时]

但对于未来,他猜想:

my enum Day ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];

Int $n where 1 <= * <= 5    # Int plus dynamic where
Day $n where 1 <= * <= 5    # 1..5

第一个where被认为是动态的,不是因为比较的性质,而是因为Int不是有限可枚举的。[第二个约束] ...可以在编译时计算集合成员资格,因为它基于Day枚举,因此 [约束,包括where子句] 被认为是静态的,尽管使用了where.


推荐阅读