Tyi*_*yil 8 oop abstract-class where abstract perl6
我正在研究Perl 6模块,Pod::To::Anything以便创建一个简单的界面来创建Perl 6 Pod格式化程序.我正在将渲染分成多个render方法,每个方法都必须处理Pod规范的给定部分.
为了确保完成基于此类的Pod格式化程序,我想添加涵盖所有可能的Pod对象的抽象方法.但是,这要求我where多次使用一个子句:
multi method render (Pod::Block::Named:D $ where *.name eq "NAME" --> Str) { … }
Run Code Online (Sandbox Code Playgroud)
我试图按如下方式实现它:
multi method render (Pod::Block::Named:D $pod where *.name eq "NAME" --> Str) { ".TH {self.pod-contents($pod)}\n" }
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试运行该程序时,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
Run Code Online (Sandbox Code Playgroud)
这是LTA,Perl 6在where这里隐藏了部件的实际内容,但这不是我的主要问题.我遇到的问题是它告诉我我实现的方法没有实现.
我把它打了一小段以确保这不是我当前代码库的特定问题:
role Foo { multi method test(%foo where *<bar>) { … } }
class Bar does Foo { multi method test(%foo where *<bar>) { "Implementation" } }
Run Code Online (Sandbox Code Playgroud)
此代码错误与类似的错误:
===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
Run Code Online (Sandbox Code Playgroud)
我的问题变成:如何where在Perl 6中使用包含子句的抽象多方法?
TL; DR这里的问题涉及where条款和P6 的基本性质.Rakudo的错误消息是LTA.您可以使用where子句,但您必须更改使用它们的方式.
where 条款和单独的模块汇编P6编译模块.它不会在模块中存储源代码,包括where子句的源代码.因此,当比较where用户源文件中use包含该模块role的where子句时,该角色中的子句与该角色中的子句相比,它不能知道它们是相同的,因此它保守地决定它不能接受它.
如上所述,编译器不会将源代码存储在已编译的模块中.所以这就是它展示的原因where { ... }.
它可以做的是where在编译角色时产生一个很棒的"你不能这样做",也许是使用实际的子句源代码,而不是在编译执行角色的类时等待不可避免地失败.
我搜索了rt.perl.org和github rakudo repo问题并且没有找到相应的票证.所以我打开了#2146.
subsets例行调度主要由名义(命名)类型驱动.
通过声明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
Run Code Online (Sandbox Code Playgroud)
重要的是要注意,存在一个老化的错误,这意味着子集不能与使用显式复合符号(%和@)声明的变量一起使用.
所以你必须使用标量印记$或削减印记.
此要求既适用于您在角色中编写的抽象方法的签名,也适用于用户编写的具体方法的签名.
您可以编写复合子集.上面的例子是一个标量子集,但你可以写,说:
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 }
Run Code Online (Sandbox Code Playgroud)
请注意,我已经切换到使用子集类型在声明中削减sigil.这是因为使用您的子集的人可能希望使用sigil绑定到新变量,就像我在最后一行中所做的那样,并且他们可能在他们的方法体中.
削减sigil而不是使用$确保sigil'd别名明显不同.例如,$当用户打算编写%sigil'd版本时,用户不会意外地写出变量名的sigil'd版本.更改名称以获得额外的安全点:
class Bar does Foo { multi method test(Nominal-Type-Hash \qux) { my %baz := qux; # use %baz from here on... } }
Run Code Online (Sandbox Code Playgroud)
因此,您可以对现有的名义类型进行子集化,以创建一个与现有类型具有不同名称的新名义类型,并且您可以(通常会)where向该新类型添加子句:
subset PTA-BN of Pod::Block::Named where *.name eq "NAME"
Run Code Online (Sandbox Code Playgroud)
现在代码可以使用PTA-BN(或您选择的任何名称)作为参数类型约束.(我认为,对于您的用户来说,除了复制where子句之外,它会更简单,更不容易出错.)
根据我们在下面的评论中的讨论,您需要添加一个is export子集:
subset PTA-BN is export of Pod::Block::Named where ...
Run Code Online (Sandbox Code Playgroud)
以及这里sub EXPORT { { PTA-BN => PTA-BN } }解释的自定义EXPORT例程().
您可以构建子集子集等子集:
subset PTA-BN2 of PTA-BN where some-other-condition;
Run Code Online (Sandbox Code Playgroud)
这将确保不仅是基本类型在运行时的价值Pod::Block::Named 和它的名字"NAME",但也认为some-other-condition是真实的了.
我提到这主要是作为一个很好的前奏......
where子句虽然例行调度主要由名义(命名)类型驱动,但需要其余的答案,有一个例外,它实际上涉及where条款!
例程调度关注where参数的一个条款,因为任何这样的子句被认为比没有条款的epsilon(一点点)更窄.
在原始代码中,角色和类方法的相应参数都有一个where子句,因此不适用.cf 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
Run Code Online (Sandbox Code Playgroud)