How can classes be made parametric in Perl 6?
通常在Perl 6中,只允许角色角色化。在这里,我们将尝试创建类,这种类(通常称为元对象)通常是不允许参数化的。
如果您尝试以天真的方式使类参数化,则会发生这种情况:
1 2 3 4 5 6 7 | bastille% perl6 -e 'class Foo[::T] {}' ===SORRY!=== Error while compiling -e Unable to parse class definition at -e:1 ------> class Foo?[::T] {} expecting any of: generic role |
但是,如果您查看
1 2 | bastille% perl6 -MNativeCall -e 'say CArray[int32].HOW.^name' Perl6::Metamodel::ClassHOW+{}+{} |
这是怎么做的?
使类参数化需要一些元编程来完成。可以像下面这样实现一个简单的参数化容器类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | use v6.d; class Container { my role ContainerImpl[::T] { has T $.value; method new(Container: T $value) { self.bless: :$value } multi method gist(Container:D: --> Str:D) { $!value.gist } multi method Str (Container:D: --> Str:D) { $!value.Str } multi method perl(Container:D: --> Str:D) { self.^name ~ '.new(' ~ $!value.perl ~ ')' } } method ^parameterize(Mu:U \\this, Mu \\T) { my $type := this.^mixin: ContainerImpl[T]; $type.^set_name: this.^name ~ '[' ~ T.^name ~ ']'; $type } } say Container[Int].new(1).perl; # OUTPUT: Container[Int].new(1) |
那么这是如何工作的?
充当
元类(例如
结合使用
TL; DR此答案是@Kaiepi的"简化"版本。它仅涵盖了从他们的答案中提取的以下所示的核心代码。编写该文件的目的是使其成为独立的解释,或者作为对它们答案的介绍或补充。
使类成为参数
名义上的问题非常广泛。但是问题的实质归结为使一个类参数化,而这正是这个答案(和@Kaiepi的答案)所关注的。
类作为一种类型,不支持现成的参数化。但是P6是完全可以元编程的。因此,您可以对一个类进行元编程以添加参数。注意这不是官方支持的技术!1
(您可以在种类级别添加参数性,以使所有类或从类派生的某种新类型都是参数性的。但是我认为这将花费大量的精力。2同时有六个使单个类参数化所需的全部工作都是相当简单的元编程。因此,这就是我们在此答案中要做的所有事情。)
编码
1 2 3 4 5 6 7 8 9 10 11 12 | class foo { my role bar[::T] {} method ^parameterize(Mu:U \\this, Mu \\T) { my $type := this.^mixin: bar[T]; $type.^set_name: this.^name ~ '[' ~ T.^name ~ ']'; $type } } say foo[Int].new.perl; # OUTPUT: foo[Int].new |
上面的代码摘自@Kaiepi的答案,而忽略了我认为不必要的内容。此答案的其余部分详细解释了代码。
A
我已经显示过的
方法名称开头的
A
声明一个带有初始
如果在编译器需要类型的地方编写
并且
1 2 3 4 5 | method ^parameterize(Mu:U \\this, Mu \\T) { my $type := this.^mixin: bar[T]; $type.^set_name: this.^name ~ '[' ~ T.^name ~ ']'; $type } |
将
我们现在已经可以生成参数化的
1 | my $type := this.^mixin: bar[T]; |
从保存在
遵循P6标称类型系统的协议
此行确保我们的新参数化类型在系统中正常运行:
1 | $type.^set_name: this.^name ~ '[' ~ T.^name ~ ']'; |
转到@Kaiepi的答案
此答案是@Kaiepi答案的简化版本。
如果实际的实现是带有参数化公共属性的类,则仅仅涵盖确保
脚注
1元模型的许多细节都不是官方P6的一部分。
2我非常有信心,通过适当的(了解胆量和元数据编程),一个人可以使所有类或从类派生的一种新类型的行为像角色一样,因为它是一种支持参数化的类型\\使用明显的语法"开箱即用":
1 | class foo[::T] { ... } |
3我非常赞同@Kaiepi的决定,不使用