我正在研究 Perl 6 模块, Pod::To::Anything
,努力制作一个简单的界面来创建 Perl 6 Pod 格式化程序。我将渲染分成多个 render
方法,每个方法都必须处理 the Pod specification 的给定部分.
为了确保基于此类的 Pod 格式化程序是完整的,我想添加涵盖所有可能的 Pod 对象的抽象方法。但是,这需要我使用 where
条款几次:
multi method render (Pod::Block::Named:D $ where *.name eq "NAME" --> Str) { … }
我尝试按如下方式实现它:
multi method render (Pod::Block::Named:D $pod where *.name eq "NAME" --> Str) { ".TH {self.pod-contents($pod)}\n" }
但是,当我尝试运行该程序时,Perl 6 提示没有实现一个方法:
===SORRY!=== Error while compiling /home/tyil/projects/personal/perl6-Pod-To-Man/lib/Pod/To/Man.pm6 (Pod::To::Man)
Multi method 'render' with signature :(Pod::To::Man $: Pod::Block::Named:D $ where { ... }, *%_ --> Str) must be implemented by Pod::To::Man because it is required by a role at /home/tyil/projects/personal/perl6-Pod-To-Man/lib/Pod/To/Man.pm6 (Pod::To::Man):7
Perl 6 隐藏了
where
的实际内容是 LTA在这里,但这不是我的主要问题。我遇到的问题是它告诉我我实现的方法没有实现。我已经降低了一点,以确保这不是我当前代码库的特定问题:
role Foo { multi method test(%foo where *<bar>) { … } }
class Bar does Foo { multi method test(%foo where *<bar>) { "Implementation" } }
此代码错误与类似错误:
===SORRY!=== Error while compiling /tmp/tmp.o2aoet3JrE/t.pl6
Multi method 'test' with signature :(Bar $: %foo where { ... }, *%_) must be implemented by Bar because it is required by a role
at /tmp/tmp.o2aoet3JrE/t.pl6:5
我的问题变成:如何使用包含
where
的抽象多方法Perl 6 中的条款?
最佳答案
TL;博士 这里的问题与 where
的基本性质有关。条款和P6。 Rakudo 的错误信息是 LTA .您可以使用 where
条款,但你必须改变你如何使用它们。where
条款和模块的单独编译
P6 编译模块。它不存储模块中的源代码,包括 where
的源代码。条款。所以当它去比较 where
用户源文件中的子句 use
s 包含 role
的模块, 与 where
角色中的条款,它不能知道它们是相同的,所以它保守地决定它不能接受它。
LTA 错误消息
如前所述,编译器不会将源代码存储在已编译的模块中。所以这就是为什么它显示 where { ... }
.
它可以做的是产生一个很棒的“你不能那样做”,也许是实际的 where
子句源代码,在编译角色时,而不是在编译执行角色的类时等待不可避免地失败。
我搜索了 rt.perl.org 和 github rakudo repo 问题,但没有找到相应的票证。所以我开通了#2146 .
P6 的标称类型调度支持使用 subset
秒
常规调度主要由名义(命名)类型驱动。
通过声明 subset
你可以给一个where
约束一个名称,然后您可以将其插入签名,从而使例程调度能够按您的意愿进行:
subset Nominal-Type where *.key eq 'bar';
role Foo { multi method test(Nominal-Type $baz) { … } }
class Bar does Foo { multi method test(Nominal-Type $baz) { "Implementation" } }
Bar.new.test: my Nominal-Type $baz = :bar
子集和符号
需要注意的是,这里有 an aged bug这意味着子集不适用于使用显式复合符号(
%
和 @
)声明的变量。所以你必须使用标量符号
$
或削减印记。此要求既适用于您在角色中编写的抽象方法的签名,也适用于您的用户编写的具体方法的签名。
复合子集
您可以编写复合子集。上面的例子是一个标量子集,但你可以这样写:
subset Nominal-Type-Hash of Hash where *<bar>;
role Foo { multi method test(Nominal-Type-Hash \qux) { … } }
class Bar does Foo { multi method test(Nominal-Type-Hash \qux) { "Implementation" } }
Bar.new.test: my %baz := my Nominal-Type-Hash \qux = { :bar }
请注意,我已切换到使用子集类型削减声明中的印记。这是因为那些使用你的子集的人可能希望绑定(bind)一个带有符号的新变量,就像我在最后一行中所做的那样,他们可能会在他们的方法体中。
削减印记而不是使用
$
确保一个 sigil'd 别名是明显不同的。例如,用户不能意外写入 $
当他们打算写 %
时变量名的标记版本签名版。也更改名称以获得额外的安全点:class Bar does Foo { multi method test(Nominal-Type-Hash \qux) { my %baz := qux; # use %baz from here on... } }
您的用例的子集
因此,您可以对现有的名义类型进行子集化以创建一个与现有类型具有不同名称的新名义类型,并且您可以(通常会)添加
where
该新类型的子句:subset PTA-BN of Pod::Block::Named where *.name eq "NAME"
现在代码可以使用
PTA-BN
(或您选择的任何名称)作为参数类型约束。 (我认为这对您的用户来说比复制 where
子句更简单,更不容易出错。)导出您的子集
根据我们在下面评论中的讨论,您需要添加
is export
到您的子集:subset PTA-BN is export of Pod::Block::Named where ...
以及自定义 EXPORT 例程(
sub EXPORT { { PTA-BN => PTA-BN } }
),如 here 所述.子集的子集
您可以建立子集等的子集子集:
subset PTA-BN2 of PTA-BN where some-other-condition;
这将确保不仅是运行时值的基础类型
Pod::Block::Named
和它的名字“NAME”还有some-other-condition
也是如此。我提到这主要是为了……
用户定义
where
条款虽然例程调度主要由名义(命名)类型驱动,因此需要本答案的其余部分,但有一个异常(exception),它实际上涉及
where
条款!日常调度注意一个
where
参数上的子句,因为任何这样的子句都被认为比没有子句窄(一点点)。在您的原始代码中,角色和类方法的相应参数都有
where
条款,因此不适用。参见 Signature Introspection .但是此功能可以在您的用例中实现一个不错的小转折。实现方法可以将您的角色提供的写在参数左侧的子集与
where
结合起来。用户在右侧写的子句,他们将在匹配时获得 dib:use Your::Module;
class User's-Class does your-role;
multi method render (PTA-BN $pod where foo --> Str) { ... } # first dibs
multi method render (PTA-BN $pod where bar --> Str) { ... } # second dibs
multi method render (PTA-BN $pod --> Str) { ... } # default
关于oop - 如何使用包含 where 的抽象多方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51570655/