As a follow up to this question about using different APIs in a single program, Liz Mattijsen suggested to use constants. Now here's a different use case: let's try to create a multi
that differentiates by API version, like this:
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
So say two.new.^api;
returns the correct api version, the caller is get-api(WithApi.new)
, so $foo
has the correct type and the correct API version, yet the multi is not called? Is there something I'm missing here?
因为评论似乎不足以讨论JJ的答案,所以我写了这篇文章,我打算将其作为对JJ和Liz的统一评论。
Let's start with JJ's final answer (I deleted a redundant line that must be a mistake). JJ wants a way to dispatch on api that is independent of any other consideration apart from a single thing that all the relevant types share. In order to get that, they've opted to put everything into dynamic
where
clauses that each do two method calls to check api and name:Liz(和我)对JJ使用动态编程而不是使用静态类型和常量感到困扰。 JJ的方法相对较慢,并且会遇到运行时错误。它在某些情况下可能有一些优点,但始终会存在严重的缺点,包括在最终答案中使用的示例JJ的情况下。
我仍然认为JJ也应该能够吃掉Liz的蛋糕之类的东西。例如,可以引入一个mixin,它附加了可以在其上分派的静态类型:
这不是DRY,但它很简单,并且在编译时进行了检查和解析。
总而言之,有时动态编程是到目前为止最好的编码方法。但是,imo,如果没有已知的基本不利因素,那么任何看起来要解决一些一般问题的方法最好都是简单静态的。
解决方案非常简单:还为别名“ 1”版本:
And that also allows you to get rid of the
where
clause in the signatures.请注意,您将无法通过它们的名字来区分它们:
如果您希望这样做,则必须设置与该类关联的元对象的名称:
然后,您将可以按名称区分:
Elizabeth Mattijsen answer above game me a hint. Signatures match symbol, not symbol name. However, when you alias (using a constant) to a new name, you still keep the name. Let's use this to have an uniform multi call where the only thing that changes is the api version:
Again following Elizabeth's answer in the previous question, constants are used for the new versions to avoid namespace clashes, but the multis will be selected solely based in api version in a relatively type-safe way, without needing to use the aliased symbols in the signature. Even if you invent a new constant to alias WithApi with any metadata, the multi will still be selected based on api version (which is what I was looking for).
给定名称空间中只能有一件事情。
I assume the whole reason you are putting the second declaration into a constant and declaring it with
my
is that it was giving you a redeclaration error.事实是,它仍然应该给您一个重新声明错误。 您的代码甚至不应该编译。
You should have to declare the second one with
anon
instead.It would then be obvious why what you are trying to do doesn't work. The second declaration is never installed into the namespace in the first place. So when you use it in the second
multi
sub it is declaring that its argument is the same type as the first class.(Even when you are using
my
in your code it isn't managing to install it into the namespace.)您假设名称空间是平面名称空间。 不是。
您可以拥有一个只有一个名称的类,但是只能使用另一个名称进行访问。
为方便起见,Raku将该类安装到名称空间中。 否则,将有很多看起来像这样的代码:
您正在尝试使用名称空间,同时尝试颠覆名称空间。 我不知道您为什么期望它能起作用。
一种让您的确切代码在编写时正常工作的快速方法是,声明第二个类是第一个类的子类。
Then when the second
multi
checks that its argument is of the first type, it still matches when you give it the second.不好
确实没有内置的方法可以完全执行您想要的操作。
您可以尝试创建一个新的元类型,该新的元类型可以创建一个将充当两个类的新类型。
我个人只是将它们都别名为独立名称。
如果要从模块加载它们: