TL;DR 这个答案是@Kaiepi++ 的“简化”版本。它仅涵盖从他们的答案中提取的下面显示的核心代码位。它的编写是为了使其可以作为独立的解释,或者作为他们答案的介绍或补充。
使类参数化
名义上的问题非常广泛。但问题的主体归结为使类参数化,这就是这个答案(和@Kaiepi's)所关注的。
类,作为kind 类型,不支持开箱即用的参数化。但是 P6 完全是metaprogrammable。所以你可以只对一个类进行元编程来添加参数。 注意。这不是官方支持的技术!1
(您可以在种类级别添加参数化,这样所有类或您从类派生的某种新类型都是参数化的。但我认为这需要相当大的努力。 2 与此同时,只需要六行相当简单的元编程就可以使单个类参数化。这就是我们在这个答案中要做的全部。)
代码
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 的回答,省略了我认为不必要的内容。这个答案的其余部分详细解释了代码。
role bar[::T]
role 像一个类一样收集属性和方法。此 SO 上下文中的主要区别在于角色是可参数化的,并且可以添加到类中,从而使类成为参数化。
[ 和 ] 之间的位是签名。 ::T 是 a type variable。签名可以像常规函数签名一样复杂。
我展示的bar 角色的主体是空的。在此技术的实际应用中,您将编写要添加到foo 类的属性和方法。这些将是需要使用参数化的属性和方法,以及可以合理包含在同一角色中的其他属性和方法。
^some-method-name
方法名称开头的^ 表明它将对其显式调用者的调用,而是“直到”调用者的“h更高的 order workings" 体现在 knowhow 对象中,知道 kind 类型的工作原理。 p>
使用初始 ^ 声明方法会导致自定义包含类的 knowhow 对象以包含该方法。
^parameterize
如果您在编译器需要类型的地方编写 foo[...],则编译器会调用(相当于)foo.^parameterize,这会变成对foo 的专有技术对象上的parameterize 的调用。
并且foo 的knowhow 对象已被定制为包含我们的方法:
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
\this
this 是什么? (\ 只是表示“削减印记”;我不是指那个方面。)
this是foo类型对象,即foo中不以^开头的普通方法中与self关联的同一类型对象。3
将bar 添加到foo 以便foo 被参数化
我们现在可以生成参数化的foo:
my $type := this.^mixin: bar[T];
从在this 中保存的未参数化foo 开始,我们将"mix" in bar 参数化为T 传递给^parameterize。
遵循 P6 标称类型系统的协议
这一行确保我们的新参数化类型与系统很好地配合:
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
继续 @Kaiepi 的回答
此答案是@Kaiepi 答案的简化版本。
如果实际实现是具有参数化公共属性的类,则仅涵盖诸如确保.perl 正常工作的问题是不够的。
脚注
1 元模型的许多细节不是official P6 的一部分。 .^parameterize 方法不是。
2 我非常有信心,通过适当的(学习胆量和)元编程,可以使所有类或从类派生的一种新类的行为都像roles,因为一种使用明显语法支持“开箱即用”参数化的类型:
class foo[::T] { ... }
3 我强烈同意@Kaiepi 的决定不使用\self 作为^ 方法的第一个参数。这将是一个谎言和阴影通常的self。想必@Kaiepi 的想法是this 经常被用作self 的同义词,但是,如果你知道P6,显然不与self 相同,因为它是第一个参数但不是调用者参数。